Im aufgeklärten Zeitalter des Jahres 2016, mit zwei neuen Standards, seit diese Frage gestellt wurde, und einem neuen gleich um die Ecke, ist es entscheidend zu wissen, dass Compiler, die den C ++ 17-Standard unterstützen , Ihren Code so kompilieren, wie er ist .
Vorlagenargumentabzug für Klassenvorlagen in C ++ 17
Hier (mit freundlicher Genehmigung von Olzhas Zhumabek über die akzeptierte Antwort) befindet sich das Papier, in dem die relevanten Änderungen des Standards aufgeführt sind.
Bedenken aus anderen Antworten ansprechen
Die aktuell am besten bewertete Antwort
Diese Antwort weist darauf hin, dass "Kopierkonstruktor und operator=
" die richtigen Vorlagenspezialisierungen nicht kennen würden.
Dies ist Unsinn, da der Standard-Kopierkonstruktor operator=
nur für einen bekannten Vorlagentyp existiert:
template <typename T>
class MyClass {
MyClass(const MyClass&) =default;
... etc...
};
// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm; // WHAT IS THIS?
*pm = m;
Wie ich in den Kommentaren bemerkt habe, gibt es hier keinen Grund für MyClass *pm
eine rechtliche Erklärung mit oder ohne die neue Form der Folgerung: MyClass
ist kein Typ (es ist eine Vorlage), daher ist es nicht sinnvoll, einen Zeiger von zu deklarieren Typ MyClass
. Hier ist eine Möglichkeit, das Beispiel zu beheben:
MyClass m(string("blah blah blah"));
decltype(m) *pm; // uses type inference!
*pm = m;
Hier pm
ist bereits vom richtigen Typ, und so ist die Folgerung trivial. Darüber hinaus ist es unmöglich, beim Aufruf des Kopierkonstruktors versehentlich Typen zu mischen :
MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));
Hier pm
wird ein Zeiger auf eine Kopie von sein m
. Hier MyClass
wird eine Kopie erstellt m
, die vom Typ ist MyClass<string>
(und nicht vom nicht vorhandenen Typ MyClass
). An dem Punkt, an dem auf den pm
Typ geschlossen wird, gibt es also genügend Informationen, um zu wissen, dass der Vorlagentyp von m
und damit der Vorlagentyp von pm
ist string
.
Darüber hinaus führt Folgendes immer zu einem Kompilierungsfehler :
MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;
Dies liegt daran, dass die Deklaration des Kopierkonstruktors nicht als Vorlage dient:
MyClass(const MyClass&);
Hier stimmt der Vorlagentyp des Copy-Konstruktor-Arguments mit dem Vorlagentyp der Klasse insgesamt überein . dh wenn MyClass<string>
instanziiert wird, MyClass<string>::MyClass(const MyClass<string>&);
wird damit instanziiert, und wenn MyClass<int>
instanziiert wird, MyClass<int>::MyClass(const MyClass<int>&);
wird instanziiert. Sofern dies nicht explizit angegeben oder ein Konstruktor mit Vorlagen deklariert ist, gibt es für den Compiler keinen Grund zur Instanziierung MyClass<int>::MyClass(const MyClass<string>&);
, was offensichtlich unangemessen wäre.
Die Antwort von Cătălin Pitiș
Pitiș gibt ein Beispiel, das ableitet Variable<int>
und Variable<double>
dann sagt:
Ich habe den gleichen Typnamen (Variable) im Code für zwei verschiedene Typen (Variable und Variable). Aus meiner subjektiven Sicht beeinträchtigt dies die Lesbarkeit des Codes ziemlich stark.
Wie im vorherigen Beispiel erwähnt, handelt es sich bei sich Variable
selbst nicht um einen Typnamen, obwohl die neue Funktion ihn syntaktisch wie einen Namen aussehen lässt.
Pitiș fragt dann, was passieren würde, wenn kein Konstruktor angegeben wird, der die entsprechende Schlussfolgerung zulässt. Die Antwort ist, dass keine Inferenz zulässig ist, da die Inferenz durch den Konstruktoraufruf ausgelöst wird . Ohne einen Konstruktoraufruf gibt es keine Schlussfolgerung .
Dies ähnelt der Frage, welche Version von foo
hier abgeleitet wird:
template <typename T> foo();
foo();
Die Antwort ist, dass dieser Code aus dem angegebenen Grund illegal ist.
MSalters Antwort
Dies ist, soweit ich das beurteilen kann, die einzige Antwort, die berechtigte Bedenken hinsichtlich der vorgeschlagenen Funktion aufwirft.
Das Beispiel ist:
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?
Die Schlüsselfrage ist, wählt der Compiler hier den vom Typ abgeleiteten Konstruktor oder den Kopierkonstruktor aus?
Wenn wir den Code ausprobieren, können wir sehen, dass der Kopierkonstruktor ausgewählt ist. So erweitern Sie das Beispiel :
Variable var(num); // infering ctor
Variable var2(var); // copy ctor
Variable var3(move(var)); // move ctor
// Variable var4(Variable(num)); // compiler error
Ich bin mir nicht sicher, wie der Vorschlag und die neue Version des Standards dies spezifizieren. es scheint durch "Abzugsleitfäden" bestimmt zu sein, die ein neues Stück Standard sind, das ich noch nicht verstehe.
Ich bin mir auch nicht sicher, warum der var4
Abzug illegal ist; Der Compilerfehler von g ++ scheint darauf hinzudeuten, dass die Anweisung als Funktionsdeklaration analysiert wird.