2015-09-27 14 views
6

Da quello che ho capito, std::forward<T>(x) equivale a static_cast<T&&>(x).C++ std :: forward <T> vs static_cast <T>

Ma da quello che ho visto, static_cast<T>(x) sembra fare la stessa cosa, come si può vedere nella seguente code

La mia domanda è quindi il motivo per cui std::forward<T> è implementato come static_cast<T&&>(x), e non static_cast<T>(x), se entrambi hanno lo stesso effetto?

+1

Non hai provato con 'int &'. – Quentin

+0

@Quentin In tal caso, restituisce sempre un lvalue. – Mikrosaft

+0

'statico_cast ' creerebbe una copia di rvalues ​​ –

risposta

10

Poiché inoltro perfetta permette di passare entrambe riferimenti r valore e riferimenti l valore. Questo viene fatto tramite reference collapsing:

T = int --> T&& = int&& 
T = int& --> T&& = int& && = int& 
T = int&& --> T&& = int&& && = int&& 

Nel tuo esempio con static_cast<T> si sta semplicemente perdendo i riferimenti R-valore. Funziona bene per i tipi primitivi (perché il passaggio di int di solito copia un valore del registro della CPU), ma è terribile per i tipi complessi perché porta alla creazione di oggetti temporanei tramite copiatori.

+0

L'ho appena controllato e in effetti chiama il costruttore di copie per valore di rvalue. Ma perché? Ho letto che se chiamiamo func() con un valore di rvalue, T è dedotto per essere int &&, quindi static_cast (x) diventerebbe static_cast (x). Allora perché crea una copia? – Mikrosaft

+2

'static_cast ' può restituire un riferimento: la risposta sopra implica che non può. Puoi chiarire che il problema si verifica solo in un "caso"? – Yakk

+0

@Mikrosaft: no, 'T' non può essere dedotto da' int &&', può essere solo 'int' o' int & '. – myaut

2

Se T&& è un riferimento rvalue, quindi T è un valore, allora static_cast<T> esegue una copia non un riferimento rvalue.

Quella copia si legherà ai riferimenti di valore (proprio come il riferimento), ma i nomi di copia/mossa potrebbero essere chiamati inutilmente, e non è un candidato per elision.

static_cast<T&&> nel frattempo verrà lanciato su un riferimento di rvalue.

Sono altrimenti identici.

0

Sono d'accordo con Yakk, ma è peggio di così.

void foo(const std::vector<int> &vec); 

template<typename T> 
void callFoo(T &&data) 
{ 
    foo(static_cast<T>(data)); 
} 

int main() 
{ 
    callFoo(std::vector<int>{/*...*/}); 
} 

Questo creerà sempre una copia. Il vettore è non spostato perché data, come espressione, è un lvalue di tipo std::vector<int>, anche se il tipo sostituito è std::vector<int>&&. Si noti che è T, non std::vector<int> &&. Morale della trama: usa la libreria standard, fa la cosa giusta e il nome forward cattura anche l'intenzione molto meglio di static_cast<something>.