La risposta accettata è una buona risposta (e l'ho svalutato). Ma ho voluto affrontare la questione in un po 'più in dettaglio:
Il nucleo della mia domanda è: Perché non scegliere l'operatore di assegnazione mossa automaticamente? Il compilatore sa che v non viene utilizzato dopo l'assegnazione , non è vero? O C++ 11 non richiede che il compilatore sia così intelligente?
Questa possibilità è stata esaminata durante la progettazione della semantica del movimento.A un estremo, si potrebbe desiderare il compilatore di fare qualche analisi statica e passare da oggetti quando possibile:
void set_name(std::string v)
{
name_ = v; // move from v if it can be proven that some_event is false?
if (some_event)
f(v);
}
definitiva chiedendo questo tipo di analisi del compilatore è molto difficile. Alcuni compilatori potrebbero essere in grado di fare la dimostrazione e altri no. Portando così al codice che non è davvero portatile.
Ok, allora che dire di alcuni casi più semplici, senza istruzioni if?
void foo()
{
X x;
Y y(x);
X x2 = x; // last use? move?
}
Beh, è difficile sapere se y.~Y()
noterà x
è stato spostato da. E in generale:
void foo()
{
X x;
// ...
// lots of code here
// ...
X x2 = x; // last use? move?
}
è difficile per il compilatore di analizzare questo per sapere se x
è veramente non è più utilizzato dopo la costruzione copia x2
.
Quindi la proposta iniziale "mossa" ha dato una regola per i movimenti impliciti che è stato davvero semplice, e molto conservatrice:
lvalue possono essere implicitamente spostati solo nei casi in cui da copiare elisione è già consentita.
Ad esempio:
#include <cassert>
struct X
{
int i_;
X() : i_(1) {}
~X() {i_ = 0;}
};
struct Y
{
X* x_;
Y() : x_(0) {}
~Y() {assert(x_ != 0); assert(x_->i_ != 0);}
};
X foo(bool some_test)
{
Y y;
X x;
if (some_test)
{
X x2;
return x2;
}
y.x_ = &x;
return x;
}
int main()
{
X x = foo(false);
}
Qui, da C++ 98/03 regole, questo programma può o non può affermare, a seconda se o meno copiare elisione a return x
succede. Se succede, il programma funziona correttamente. Se non succede, afferma il programma.
E così è stato ragionato: quando è permesso RVO, siamo già in una zona dove non ci sono garanzie per quanto riguarda il valore di x
. Quindi dovremmo essere in grado di sfruttare questo margine e passare da x
. Il rischio sembrava piccolo e il beneficio sembrava enorme. Non solo questo significherebbe che molti programmi esistenti diventerebbero molto più velocemente con una semplice ricompilazione, ma significava anche che ora potevamo tornare "muovere solo" tipi da funzioni factory. Questo è un rapporto beneficio/rischio molto elevato.
Ultimamente nel processo di standardizzazione, siamo diventati un po 'avidi e abbiamo anche detto che la mossa implicita si verifica quando si restituisce un parametro in base al valore (e il tipo corrisponde al tipo restituito). Anche qui i benefici sembrano relativamente grandi, anche se la possibilità di interruzione del codice è leggermente maggiore poiché questo non è il caso in cui RVO era (o è) legale. Ma non ho una dimostrazione di codice di rottura per questo caso.
Quindi in ultima analisi, la risposta alla tua domanda di base è che il disegno originale della semantica mossa ha preso una strada molto conservatore rispetto a rompere il codice esistente. Se non fosse stato, sarebbe sicuramente stato abbattuto in commissione. Più tardi nel processo, ci sono stati alcuni cambiamenti che hanno reso il design un po 'più aggressivo. Ma a quel tempo la proposta principale era saldamente radicata nello standard con un sostegno maggioritario (ma non unanime).
non vedo un costruttore mossa da qualche parte? – Corbin
@NicolBolas: questo non è correlato in remoto a questa domanda. Sta chiedendo di spostare un membro 'std :: string' della sua classe. Non sta cercando di spostare la classe. –
@MooingDuck: Oh. Bene, non importa poi. Non posso davvero fare molto per il voto vicino se ... –