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 de Fooressemble en fait à ceci:
Foo(int&&)
Foo(2)fonctionne parce que 2c'est une valeur.
Foo(x)ne le fait pas car il xs'agit d'une valeur l qui ne peut pas être liée int&&. Vous pouvez faire std::move(x)pour le caster dans le type approprié ( démo )
Foo<int&>(x)fonctionne très bien car le constructeur devient Foo(int&)dû à des règles d'effondrement de référence; au départ, c'est ce Foo((int&)&&)qui s'effondre Foo(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:
template<typename T>
struct Foo {
Foo(T&&) {}
};
template<typename T>
Foo<T> MakeFoo(std::add_rvalue_reference_t<T> value)
{
return Foo<T>(std::move(value));
}
//...
auto f = MakeFoo(x);
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 instanciation Foo<int>, le constructeur ressemble à Foo(int&&)une rvalue-reference en d'autres termes. D'où l'utilisation de ce qui add_rvalue_reference_tprécède.
Évidemment, cela ne fonctionne pas.
Lorsque vous avez ajouté votre guide de déduction «redondant»:
template<typename T>
Foo(T&&) -> Foo<T>;
Vous avez permis au compilateur de distinguer que, malgré toute référence attachée à Tdans le constructeur ( int&, const int&ou int&&etc.), vous avez voulu le type déduit de la classe sans référence (juste T). 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:
template<class U>
Foo<U> MakeFoo(U&& u)
{
return Foo<U>(std::forward<U>(u));
}
// ...
auto f = MakeFoo(x);
(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 ainsi MakeFoo(x)).
Cela permet U&&de devenir int&et Tde devenir simplementint