Il problema qui è che, dal momento che la classe è templato su T
, nel costruttore Foo(T&&)
stiamo non eseguendo tipo detrazione; Abbiamo sempre un riferimento di valore r. Cioè, il costruttore per Foo
effettivamente assomiglia a questo:
Foo(int&&)
Foo(2)
funziona perché 2
è un valore.
Foo(x)
non perché x
è un valore che non può essere associato int&&
. Si potrebbe fare std::move(x)
per lanciarlo nel tipo appropriato ( demo )
Foo<int&>(x)
funziona bene perché il costruttore diventa Foo(int&)
dovuto alle regole di crollo di riferimento; inizialmente è quello Foo((int&)&&)
che crolla Foo(int&)
secondo lo standard.
Per quanto riguarda la tua guida alla detrazione "ridondante": inizialmente esiste una guida alla detrazione del modello predefinita per il codice che sostanzialmente agisce come una funzione di supporto in questo modo:
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);
Questo perché lo standard impone che questo metodo (fittizio) di modello abbia gli stessi parametri del modello della classe (Just T
) seguito da tutti i parametri del modello del costruttore (nessuno in questo caso; il costruttore non è modellato). Quindi, i tipi dei parametri della funzione sono gli stessi di quelli nel costruttore. Nel nostro caso, dopo un'istanza Foo<int>
, il costruttore assomiglia a Foo(int&&)
un riferimento di valore in altre parole. Da qui l'utilizzo di cui add_rvalue_reference_t
sopra.
Ovviamente questo non funziona.
Quando hai aggiunto la tua guida alla detrazione "ridondante":
template<typename T>
Foo(T&&) -> Foo<T>;
È permesso il compilatore di distinguere che, nonostante qualsiasi tipo di riferimento collegato a T
nel costruttore ( int&
, const int&
, o int&&
, ecc), si intende il tipo di dedurre per la classe di essere senza di riferimento (solo T
). Questo perché improvvisamente ci stiamo eseguendo l'inferenza dei tipi.
Ora generiamo un'altra funzione di supporto (fittizia) che assomiglia a questa:
template<class U>
Foo<U> MakeFoo(U&& u)
{
return Foo<U>(std::forward<U>(u));
}
// ...
auto f = MakeFoo(x);
(Le nostre chiamate al costruttore vengono reindirizzate alla funzione di supporto ai fini della deduzione dell'argomento del modello di classe, così Foo(x)
diventa MakeFoo(x)
).
Questo permette U&&
di diventare int&
e T
diventare semplicementeint