A questão aqui é que, uma vez que a classe é modelada em T
, no construtor Foo(T&&)
estamos não realizando tipo dedução; Sempre temos uma referência de valor-r. Ou seja, o construtor para Foo
se parece com isso:
Foo(int&&)
Foo(2)
funciona porque 2
é um pré-valor.
Foo(x)
não porque x
é um valor l que não pode ser vinculado int&&
. Você pode fazer isso std::move(x)
para transmiti-lo ao tipo apropriado ( demo )
Foo<int&>(x)
funciona muito bem porque o construtor se torna Foo(int&)
devido a regras de recolhimento de referência; inicialmente é o Foo((int&)&&)
que cai de Foo(int&)
acordo com o padrão.
Em relação ao seu guia de dedução "redundante": Inicialmente, há um guia de dedução de modelo padrão para o código que basicamente atua como uma função auxiliar da seguinte forma:
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);
Isso ocorre porque o padrão determina que esse método de modelo (fictício) possua os mesmos parâmetros de modelo que a classe (Just T
), seguidos por quaisquer parâmetros de modelo que o construtor (nenhum neste caso; o construtor não tem modelo). Em seguida, os tipos dos parâmetros de função são os mesmos do construtor. No nosso caso, após instanciar Foo<int>
, o construtor se parece com Foo(int&&)
uma referência de valor em outras palavras. Daí o uso add_rvalue_reference_t
acima.
Obviamente, isso não funciona.
Quando você adicionou seu guia de dedução "redundante":
template<typename T>
Foo(T&&) -> Foo<T>;
Você permitiu que o compilador para distinguir que, apesar de qualquer tipo de referência ligado a T
no construtor ( int&
, const int&
ou int&&
etc.), que pretendia o tipo inferido para a classe para ser sem referência (apenas T
). Isso ocorre porque de repente somos realizando inferência de tipo.
Agora, geramos outra função auxiliar (fictícia) que se parece com isso:
template<class U>
Foo<U> MakeFoo(U&& u)
{
return Foo<U>(std::forward<U>(u));
}
// ...
auto f = MakeFoo(x);
(Nossas chamadas para o construtor são redirecionadas para a função auxiliar para fins de dedução do argumento do modelo de classe, Foo(x)
tornando - seMakeFoo(x)
).
Isso permite U&&
tornar-se int&
e T
tornar-se simplesmenteint