2013-01-16 7 views
10

Nell'articolo Artima relativo al riferimento del valore in C++ (http://www.artima.com/cppsource/rvalue.html) sono presenti le parole: Ecco perché è necessario dire move (x) invece di solo x quando si passa alla classe base. Questa è una caratteristica di sicurezza chiave della semantica del movimento progettata per evitare di spostarsi accidentalmente due volte da una variabile denominata.Riferimento Rvalue: perché i valori non vengono spostati implicitamente?

Non riesco a pensare alla situazione quando una tale mossa doppia può funzionare. Puoi dare un esempio di questo? In altre parole, cosa potrebbe andare storto se tutti i membri di T&& fossero riferimenti di riferimento e non solo riferimenti?

risposta

16

consideri questo scenario:

void foo(std::string x) {} 
void bar(std::string y) {} 

void test(std::string&& str) 
{ 
    // to be determined 
} 

vogliamo chiamare foo con str, poi bar con str, entrambi con lo stesso valore. Il modo migliore per farlo è:

foo(str); // copy str to x 
bar(std::move(str)); // move str to y; we move it because we're done with it 

E sarebbe un errore per fare questo:

foo(std::move(str)); // move str to x 
bar(std::move(str)); // move str to y...er, except now it's empty 

Perché dopo la prima mossa il valore di str non è specificato.

Quindi nella progettazione dei riferimenti di valore, questa mossa implicita non è presente. Se lo fosse, la nostra soluzione migliore non funzionerebbe perché la prima menzione di str sarebbe invece std::move(str).

3

Il mio modo di vedere è che se abbiamo avuto qualche riferimento rvalue x:

T&& x = ...; 

e abbiamo chiamato qualche funzione utilizzando x come parametro:

f(x) 

Abbiamo bisogno di qualche modo per dire a f se può o meno danneggiare x (o "assumere la proprietà di x" o "è l'ultimo client che usa x").

Un modo per progettare questo sarebbe quello di qualificare ogni chiamata:

f(yours_now(x)) // ok to damage 
f(still_mine(x)) // dont damage 

ed effettuare la chiamata non qualificata illegale.

Un altro modo sarebbe quello di rendere un modo di default:

O:

f(yours_now(x)) // ok to damage 
f(x) // dont damage 

o

f(x) // ok to damage 
f(still_mine(x)) // dont damage 

Quindi, se siamo d'accordo qualificazione di ogni utilizzo è troppo ingombrante e dovremmo predefinite in un modo, che è il migliore? Bene, guardiamo al costo di scegliere accidentalmente il valore predefinito in entrambi i casi:

Nel primo caso è stato corretto il danno, ma abbiamo accidentalmente detto che non lo era. In questo caso perdiamo le prestazioni perché è stata effettuata una copia non necessaria, ma a parte questo non è un grosso problema.

Nel secondo caso non era giusto danneggiare un oggetto, ma abbiamo accidentalmente detto che lo era.Ciò potrebbe causare un bug logico difficile da rilevare nel programma, dato che x è ora in uno stato danneggiato quando f restituisce, ma l'autore si aspettava che non lo fosse.

Quindi il primo caso è quello che è stato scelto perché è "più sicuro".