À l'ère éclairée de 2016, avec deux nouvelles normes à notre actif depuis que cette question a été posée et une nouvelle juste au coin de la rue, la chose cruciale à savoir est que les compilateurs prenant en charge la norme C ++ 17 compileront votre code tel quel. .
Déduction d'argument de modèle pour les modèles de classe en C ++ 17
Voici (grâce à une modification par Olzhas Zhumabek de la réponse acceptée) le document détaillant les changements pertinents apportés à la norme.
Répondre aux préoccupations d'autres réponses
La réponse actuelle la mieux notée
Cette réponse souligne que "copier le constructeur et operator=
" ne connaîtrait pas les spécialisations de modèle correctes.
C'est absurde, car le constructeur de copie standard operator=
n'existe que pour un type de modèle connu :
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;
Ici, comme je l' ai mentionné dans les commentaires, il n'y a aucune raison pour MyClass *pm
être une déclaration juridique avec ou sans la nouvelle forme d'inférence: MyClass
n'est pas un type (il est un modèle), il n'a pas de sens de déclarer un pointeur de type MyClass
. Voici une façon possible de corriger l'exemple:
MyClass m(string("blah blah blah"));
decltype(m) *pm; // uses type inference!
*pm = m;
Ici, pm
est déjà du type correct, et donc l'inférence est triviale. De plus, il est impossible de mélanger accidentellement des types lors de l'appel du constructeur de copie:
MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));
Ici, pm
sera un pointeur vers une copie de m
. Ici, MyClass
est en cours de construction de copie - m
qui est de type MyClass<string>
(et non de type inexistant MyClass
). Ainsi, au moment où pm
le type de s est déduit, il y a suffisamment d'informations pour savoir que le type de modèle de m
, et donc le type de modèle de pm
, est string
.
De plus, ce qui suit provoquera toujours une erreur de compilation :
MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;
C'est parce que la déclaration du constructeur de copie n'est pas basée sur un modèle:
MyClass(const MyClass&);
Ici, le type de modèle de l'argument du constructeur de copie correspond au type de modèle de la classe dans son ensemble; c'est-à-dire, quand MyClass<string>
est instancié, MyClass<string>::MyClass(const MyClass<string>&);
est instancié avec lui, et quand MyClass<int>
est instancié, MyClass<int>::MyClass(const MyClass<int>&);
est instancié. À moins qu'il ne soit explicitement spécifié ou qu'un constructeur basé sur un modèle soit déclaré, il n'y a aucune raison pour que le compilateur instancie MyClass<int>::MyClass(const MyClass<string>&);
, ce qui serait évidemment inapproprié.
La réponse de Cătălin Pitiș
Pitiș donne un exemple en déduisant Variable<int>
et Variable<double>
, puis déclare:
J'ai le même nom de type (variable) dans le code pour deux types différents (variable et variable). De mon point de vue subjectif, cela affecte sensiblement la lisibilité du code.
Comme indiqué dans l'exemple précédent, Variable
lui-même n'est pas un nom de type, même si la nouvelle fonctionnalité le fait ressembler syntaxiquement.
Pitiș demande alors ce qui se passerait si aucun constructeur n'est donné qui permettrait l'inférence appropriée. La réponse est qu'aucune inférence n'est autorisée, car l'inférence est déclenchée par l' appel du constructeur . Sans appel de constructeur, il y a pas d'inférence .
Cela revient à demander quelle version de foo
est déduite ici:
template <typename T> foo();
foo();
La réponse est que ce code est illégal, pour la raison indiquée.
Réponse de MSalter
C'est, pour autant que je sache, la seule réponse pour soulever une préoccupation légitime concernant la fonctionnalité proposée.
L'exemple est:
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?
La question clé est la suivante: le compilateur sélectionne-t -il ici le constructeur déduit du type ou la copie constructeur de ?
En essayant le code, nous pouvons voir que le constructeur de copie est sélectionné. Pour développer l'exemple :
Variable var(num); // infering ctor
Variable var2(var); // copy ctor
Variable var3(move(var)); // move ctor
// Variable var4(Variable(num)); // compiler error
Je ne sais pas comment la proposition et la nouvelle version de la norme le précisent; il semble être déterminé par des «guides de déduction», qui sont un nouveau morceau de standard que je ne comprends pas encore.
Je ne sais pas non plus pourquoi la var4
déduction est illégale; l'erreur du compilateur de g ++ semble indiquer que l'instruction est analysée comme une déclaration de fonction.