El problema aquí es que, puesto que la clase está en templated T, en el constructor Foo(T&&)que estamos no realizando el tipo de deducción; Siempre tenemos una referencia de valor r. Es decir, el constructor para Foorealmente se ve así:
Foo(int&&)
Foo(2)funciona porque 2es un prvalue.
Foo(x)no lo hace porque xes un valor que no se puede unir int&&. Podrías hacer std::move(x)para lanzarlo al tipo apropiado ( demo )
Foo<int&>(x)funciona bien porque el constructor se Foo(int&)debe a reglas de colapso de referencia; inicialmente es lo Foo((int&)&&)que colapsa Foo(int&)según el estándar.
Con respecto a su guía de deducción "redundante": Inicialmente hay una guía de deducción de plantilla predeterminada para el código que básicamente actúa como una función auxiliar de esta manera:
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);
Esto se debe a que el estándar dicta que este método de plantilla (ficticio) tiene los mismos parámetros de plantilla que la clase (Just T) seguido de cualquier parámetro de plantilla como el constructor (ninguno en este caso; el constructor no tiene plantilla). Entonces, los tipos de los parámetros de la función son los mismos que los del constructor. En nuestro caso, después de crear instancias Foo<int>, el constructor se ve como Foo(int&&)una referencia de valor en otras palabras. De ahí el uso de lo add_rvalue_reference_tanterior.
Obviamente esto no funciona.
Cuando agregó su guía de deducción "redundante":
template<typename T>
Foo(T&&) -> Foo<T>;
Permitieron que el compilador distinguir que, a pesar de cualquier tipo de referencia correspondiente a Ten el constructor ( int&, const int&o int&&etc.), que pretendía el tipo inferido para la clase de ser sin referencia (justo T). Esto se debe a que de repente estamos realizando inferencia de tipos.
Ahora generamos otra función auxiliar (ficticia) que se ve así:
template<class U>
Foo<U> MakeFoo(U&& u)
{
return Foo<U>(std::forward<U>(u));
}
// ...
auto f = MakeFoo(x);
(Nuestras llamadas al constructor se redirigen a la función auxiliar con el propósito de deducir el argumento de la plantilla de clase, por lo que se Foo(x)convierte en MakeFoo(x)).
Esto permite U&&volverse int&y Tvolverse simplementeint