Étant donné la structure de modèle suivante:
template<typename T>
struct Foo {
Foo(T&&) {}
};
Cela compile et T
se déduit comme int
:
auto f = Foo(2);
Mais cela ne compile pas: https://godbolt.org/z/hAA9TE
int x = 2;
auto f = Foo(x);
/*
<source>:12:15: error: no viable constructor or deduction guide for deduction of template arguments of 'Foo'
auto f = Foo(x);
^
<source>:7:5: note: candidate function [with T = int] not viable: no known conversion from 'int' to 'int &&' for 1st argument
Foo(T&&) {}
^
*/
Cependant, Foo<int&>(x)
est accepté.
Mais lorsque j'ajoute un guide de déduction défini par l'utilisateur apparemment redondant, cela fonctionne:
template<typename T>
Foo(T&&) -> Foo<T>;
Pourquoi ne peut-on pas T
être déduit int&
sans un guide de déduction défini par l'utilisateur?
c++
templates
language-lawyer
jtbandes
la source
la source
Foo<T<A>>
Réponses:
Je pense que la confusion survient ici car il existe une exception spécifique pour les guides de déduction synthétisés concernant les références de transfert.
Il est vrai que la fonction candidate aux fins de la déduction d'arguments de modèle de classe générée à partir du constructeur et celle générée à partir du guide de déduction défini par l'utilisateur sont exactement les mêmes, c'est-à-dire:
mais pour celui généré par le constructeur, il
T&&
s'agit d'une simple référence rvalue, alors qu'il s'agit d'une référence de transfert dans le cas défini par l'utilisateur. Ceci est spécifié par [temp.deduct.call] / 3 de la norme C ++ 17 (projet N4659, mettez l'accent sur le mien):Par conséquent, le candidat synthétisé à partir du constructeur de classe ne déduira pas
T
comme s'ilT
provenait d'une référence de transfert (qui pourrait en déduire être une référence lvalue, de sorte qu'ilT&&
s'agit également d'une référence lvalue), mais au lieu de cela, il ne déduiraT
que comme non-référence, de sorte queT&&
c'est toujours une référence rvalue.la source
Foo<int>(...)
ou justeFoo(...)
, ce qui n'est pas le cas avec les références de transfert (qui pourraient en déduire à laFoo<int&>
place.Le problème ici est que, puisque la classe est basée sur un modèle
T
, dans le constructeur,Foo(T&&)
nous n'effectuons pas de déduction de type; Nous avons toujours une référence de valeur r. Autrement dit, le constructeur deFoo
ressemble en fait à ceci:Foo(2)
fonctionne parce que2
c'est une valeur.Foo(x)
ne le fait pas car ilx
s'agit d'une valeur l qui ne peut pas être liéeint&&
. Vous pouvez fairestd::move(x)
pour le caster dans le type approprié ( démo )Foo<int&>(x)
fonctionne très bien car le constructeur devientFoo(int&)
dû à des règles d'effondrement de référence; au départ, c'est ceFoo((int&)&&)
qui s'effondreFoo(int&)
selon la norme.En ce qui concerne votre guide de déduction "redondant": Au départ, il existe un guide de déduction de modèle par défaut pour le code qui agit essentiellement comme une fonction d'aide comme ceci:
Cela est dû au fait que la norme dicte que cette méthode de modèle (fictive) a les mêmes paramètres de modèle que la classe (Just
T
), suivis de tout paramètre de modèle comme le constructeur (aucun dans ce cas; le constructeur n'est pas basé sur des modèles). Ensuite, les types des paramètres de fonction sont les mêmes que ceux du constructeur. Dans notre cas, après instanciationFoo<int>
, le constructeur ressemble àFoo(int&&)
une rvalue-reference en d'autres termes. D'où l'utilisation de ce quiadd_rvalue_reference_t
précède.Évidemment, cela ne fonctionne pas.
Lorsque vous avez ajouté votre guide de déduction «redondant»:
Vous avez permis au compilateur de distinguer que, malgré toute référence attachée à
T
dans le constructeur (int&
,const int&
ouint&&
etc.), vous avez voulu le type déduit de la classe sans référence (justeT
). En effet , nous soudainement nous accomplissons l' inférence de type.Maintenant, nous générons une autre fonction d'aide (fictive) qui ressemble à ceci:
(Nos appels au constructeur sont redirigés vers la fonction d'assistance aux fins de déduction des arguments du modèle de classe,
Foo(x)
devient ainsiMakeFoo(x)
).Cela permet
U&&
de devenirint&
etT
de devenir simplementint
la source
x
est une valeur qui ne peut pas se lier àint&&
" mais quelqu'un qui ne comprend pas sera perplexe quiFoo<int&>(x)
pourrait fonctionner mais n'a pas été compris automatiquement - je pense que nous voulons tous une compréhension plus profonde de pourquoi.