ma question aujourd'hui est assez simple: pourquoi le compilateur ne peut-il pas déduire les paramètres de modèle à partir des constructeurs de classe, autant qu'il peut le faire à partir des paramètres de fonction? Par exemple, pourquoi le code suivant ne pourrait-il pas être valide:
template<typename obj>
class Variable {
obj data;
public: Variable(obj d)
{
data = d;
}
};
int main()
{
int num = 2;
Variable var(num); //would be equivalent to Variable<int> var(num),
return 0; //but actually a compile error
}
Comme je l'ai dit, je comprends que ce n'est pas valide, alors ma question est de savoir pourquoi n'est-ce pas? Autoriser cela créerait-il des trous syntaxiques majeurs? Existe-t-il une instance où l'on ne voudrait pas de cette fonctionnalité (où inférer un type causerait des problèmes)? J'essaie juste de comprendre la logique derrière l'autorisation de l'inférence de modèle pour les fonctions, mais pas pour les classes correctement construites.
template<class T> Variable<T> make_Variable(T&& p) {return Variable<T>(std::forward<T>(p));}
Réponses:
Je pense que ce n'est pas valide car le constructeur n'est pas toujours le seul point d'entrée de la classe (je parle de constructeur de copie et d'opérateur =). Supposons donc que vous utilisiez votre classe comme ceci:
Je ne sais pas s'il serait si évident pour l'analyseur de savoir quel type de modèle est MyClass pm;
Je ne sais pas si ce que j'ai dit a du sens, mais n'hésitez pas à ajouter quelques commentaires, c'est une question intéressante.
C ++ 17
Il est admis que C ++ 17 aura une déduction de type à partir des arguments du constructeur.
Exemples:
Papier accepté .
la source
MyClass *pm
ici serait invalide pour la même raison qu'une fonction déclaréetemplate <typename T> void foo();
ne peut pas être appelée sans spécialisation explicite.Vous ne pouvez pas faire ce que vous demandez pour les raisons que d'autres personnes ont adressées, mais vous pouvez le faire:
ce qui à toutes fins utiles est la même chose que vous demandez. Si vous aimez l'encapsulation, vous pouvez faire de make_variable une fonction membre statique. C'est ce que les gens appellent constructeur nommé. Donc, non seulement il fait ce que vous voulez, mais il s'appelle presque ce que vous voulez: le compilateur déduit le paramètre de modèle du constructeur (nommé).
NB: tout compilateur raisonnable optimisera l'objet temporaire lorsque vous écrivez quelque chose comme
la source
auto v = make_variable(instance)
pour ne pas avoir à spécifier le typestatic
membre ... Pensez-y pendant une seconde. Cela mis à part: les fonctions make gratuites étaient en effet la solution, mais c'est beaucoup de passe-partout redondant, que pendant que vous le tapez, vous savez juste que vous ne devriez pas avoir à le faire car le compilateur a accès à toutes les informations que vous répétez. .. et heureusement C ++ 17 canonise cela.À 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 :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 typeMyClass
. Voici une façon possible de corriger l'exemple: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:Ici,
pm
sera un pointeur vers une copie dem
. Ici,MyClass
est en cours de construction de copie -m
qui est de typeMyClass<string>
(et non de type inexistantMyClass
). 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 dem
, et donc le type de modèle depm
, eststring
.De plus, ce qui suit provoquera toujours une erreur de compilation :
C'est parce que la déclaration du constructeur de copie n'est pas basée sur un modèle:
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 quandMyClass<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 instancieMyClass<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>
etVariable<double>
, puis déclare: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: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:
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 :
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.la source
var4
est juste un cas de "l'analyse la plus vexante" (non liée à la déduction d'argument modèle). Nous avions l'habitude d'utiliser simplement des parens supplémentaires pour cela, mais ces jours-ci, je pense que l'utilisation d'accolades pour désigner sans ambiguïté la construction est le conseil habituel.Variable var4(Variable(num));
est traité comme une déclaration de fonction? Si tel est le cas, pourquoiVariable(num)
une spécification de paramètre est -elle valide?Toujours manquant: cela rend le code suivant assez ambigu:
la source
Supposons que le compilateur supporte ce que vous avez demandé. Alors ce code est valide:
Maintenant, 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. Avoir le même nom de type pour deux types différents dans le même espace de noms me semble trompeur.
Mise à jour ultérieure: Autre chose à considérer: la spécialisation partielle (ou complète) des modèles.
Et si je me spécialise dans Variable et que je ne fournis aucun constructeur comme prévu?
Donc j'aurais:
Ensuite, j'ai le code:
Que doit faire le compilateur? Utilisez la définition de classe Variable générique pour déduire qu'il s'agit d'une variable, puis découvrez que Variable ne fournit pas un constructeur de paramètre?
la source
Le C ++ 03 et le standard C ++ 11 n'autorisent pas la déduction d'arguments de modèle à partir des paramètres passés au constructeur.
Mais il existe une proposition de "Déduction de paramètres de modèle pour les constructeurs" afin que vous obteniez bientôt ce que vous demandez. Edit: en effet, cette fonctionnalité a été confirmée pour C ++ 17.
Voir: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3602.html et http://www.open-std.org/jtc1/sc22/wg21/docs/ papiers / 2015 / p0091r0.html
la source
De nombreuses classes ne dépendent pas des paramètres du constructeur. Il n'y a que quelques classes qui n'ont qu'un seul constructeur et qui sont paramétrées en fonction du ou des types de ce constructeur.
Si vous avez vraiment besoin d'une inférence de modèle, utilisez une fonction d'assistance:
la source
La déduction des types est limitée aux fonctions de modèle dans le C ++ actuel, mais on s'est rendu compte depuis longtemps que la déduction de types dans d'autres contextes serait très utile. D'où C ++ 0x
auto
.Bien que ce que vous suggérez ne soit pas possible en C ++ 0x, ce qui suit montre que vous pouvez vous en approcher assez:
la source
Vous avez raison, le compilateur pourrait facilement deviner, mais ce n'est pas dans la norme ou C ++ 0x pour autant que je sache, vous devrez donc attendre au moins 10 ans de plus (taux de rotation fixe des normes ISO) avant que les fournisseurs de compilateurs ajoutent cette fonctionnalité
la source
Regardons le problème en référence à une classe que tout le monde devrait connaître - std :: vector.
Tout d'abord, une utilisation très courante du vecteur est d'utiliser le constructeur qui ne prend aucun paramètre:
Dans ce cas, évidemment aucune inférence ne peut être effectuée.
Une deuxième utilisation courante consiste à créer un vecteur prédimensionné:
Ici, si l'inférence était utilisée:
nous obtenons un vecteur d'entiers, pas de chaînes, et vraisemblablement il n'est pas dimensionné!
Enfin, considérez les constructeurs qui acceptent plusieurs paramètres - avec "inférence":
Quel paramètre doit être utilisé pour l'inférence? Nous aurions besoin d'un moyen de dire au compilateur que ce devrait être le deuxième.
Avec tous ces problèmes pour une classe aussi simple que vectorielle, il est facile de comprendre pourquoi l'inférence n'est pas utilisée.
la source
Faire du ctor un modèle la Variable ne peut avoir qu'une seule forme mais plusieurs cteurs:
Voir? Nous ne pouvons pas avoir plusieurs membres Variable :: data.
la source
Voir la déduction d'argument de modèle C ++ pour plus d'informations à ce sujet.
la source