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 *pmeine 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 pmist 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 pmwird ein Zeiger auf eine Kopie von sein m. Hier MyClasswird eine Kopie erstellt m, die vom Typ ist MyClass<string>(und nicht vom nicht vorhandenen Typ MyClass). An dem Punkt, an dem auf den pmTyp geschlossen wird, gibt es also genügend Informationen, um zu wissen, dass der Vorlagentyp von mund damit der Vorlagentyp von pmist 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 Variableselbst 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 foohier 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 var4Abzug illegal ist; Der Compilerfehler von g ++ scheint darauf hinzudeuten, dass die Anweisung als Funktionsdeklaration analysiert wird.