2016-07-07 20 views
15

Diamo una funzione chiamata Y che sovraccarica:Perché std :: forward restituisce static_cast <T&&> e non statico_cast <T>?

void Y(int& lvalue) 
{ cout << "lvalue!" << endl; } 

void Y(int&& rvalue) 
{ cout << "rvalue!" << endl; } 

Ora, definiamo una funzione template che si comporta come std :: avanti

template<class T> 
void f(T&& x) 
{ 
    Y(static_cast<T&&>(x)); // Using static_cast<T&&>(x) like in std::forward 
} 

Ora guardate la main()

int main() 
{ 
    int i = 10; 

    f(i);  // lvalue >> T = int& 
    f(10);  // rvalue >> T = int&& 
} 

Come previsto, l'output è

lvalue! 
rvalue! 

Ora tornare alla funzione modello f() e sostituire static_cast<T&&>(x) con static_cast<T>(x). Vediamo l'output:

lvalue! 
rvalue! 

È lo stesso! Perché? Se sono uguali, allora perché std::forward<> restituisce un cast da x a T&&?

risposta

23

La classificazione lvalue vs rvalue rimane la stessa, ma l'effetto è molto diverso (e la categoria di valore non cambia, anche se non in un modo osservabile nel tuo esempio). Andiamo oltre i quattro casi:

template<class T> 
void f(T&& x) 
{ 
    Y(static_cast<T&&>(x)); 
} 

template<class T> 
void g(T&& x) 
{ 
    Y(static_cast<T>(x)); 
} 

Se chiamiamo f con un lvalue, T sarà dedurre alcuni X&, quindi il riferimento getto crolla X& && ==> X&, così si finisce con lo stesso lvalue e non cambia nulla.

Se chiamiamo f con rvalue, T sarà dedurre alcuni X modo cast solo converte x ad un riferimento rvalue a x, quindi diventa un rvalue (specificamente, un xValue).

Se chiamiamo g con un lvalue, accadono sempre le stesse cose. Non è necessario alcun riferimento al collasso, dal momento che stiamo usando solo lo T == X&, ma il cast è ancora un no-op e alla fine continuiamo con lo stesso lvalue.

Ma se chiamiamo g con una rvalue, abbiamo static_cast<T>(x) che sarà copiarex. Quella copia è un valore di rvalue (come verifica il test - eccetto ora è un valore di valore invece di un valore x), ma è una copia extra e non necessaria al meglio e sarebbe un errore di compilazione (se T è mobile ma non copiabile) nel peggiore dei casi. Con static_cast<T&&>(x), abbiamo eseguito il casting su un riferimento, che non richiama una copia.

Ecco perché facciamo T&&.

+0

Spiegherete perché la copia si verifica in un static_cast? – gedamial

+0

La categoria del valore non è la stessa. La categoria di valore * gruppo * è la stessa ("valore_valore"), ma le categorie di valori differiscono (valore vs valore x). –

+2

@gedamial: che altro avrebbe fatto? Quando converti un 'T1' in un' T2', i dati vengono copiati (tramite trasformazione) in un nuovo oggetto. Questo può essere evitato solo con i tipi di riferimento .... ([o, per 'typeid (T1) == typeid (T2)', utilizzando Visual Studio nella sua riduttiva modalità predefinita non conforme] (http: // stackoverflow. COM/a/37969300/560648)). –

Problemi correlati