2016-02-26 21 views
5

L'idea della semantica del movimento è che puoi afferrare tutto da un altro oggetto temporaneo (a cui fa riferimento un riferimento rvalue) e archiviare quel "tutto" nel tuo oggetto. Ciò aiuta a evitare la copia profonda in cui una singola costruzione di cose è sufficiente - quindi costruisci le cose in un oggetto rvalue e poi spostali semplicemente sul tuo oggetto long life.Perché gli oggetti Lvalue C++ non possono essere associati a riferimenti di riferimento (&&)?

Perché il C++ non consente agli oggetti lvalue di eseguire il bind di riferimento? Entrambi mi permettono di cambiare l'oggetto di riferimento, quindi non c'è alcuna differenza per me in termini di accesso agli interni dell'oggetto referenziato.

L'unica ragione che posso indovinare è il sovraccarico delle funzioni di problemi di ambiguità.

+1

Stai chiedendo perché non riesci a "svuotare foo (vector & bar) {} foo (crea qui il vettore temporaneo)?"? – NathanOliver

+1

Conosci 'std :: move'? – Jarod42

risposta

15

Ma perché C++ non consente il riferimento di riferimenti ai valori degli oggetti lvalue vincolanti?

Supponendo che vuoi dire "Perché non C++ consentono vincolante riferimenti rvalue a lvalue oggetti": lo fa. Semplicemente non è automatico, quindi devi usare std::move per renderlo esplicito.

Perché? Perché altrimenti una chiamata di funzione innocua può sorprendentemente distruggere qualcosa che non mi aspettavo che:

Class object(much,state,many,members,wow); 
looks_safe_to_me(object); 
// oh no, it destructively copied my object! 

vs.

Class object(much,state,many,members,wow); 
obviously_destructive(std::move(object)); 
// same result, but now the destruction is explicit and expected 

Una nota sulla duplicazione distruttiva: perché dico distruttivo e distruzione precedente, non intendo che il distruttore dell'oggetto termini la sua durata: solo che il suo stato interno è stato spostato in una nuova istanza. È ancora un oggetto valido, ma non mantiene più lo stesso costoso stato a cui era abituato.


Una nota sulla terminologia: vediamo se siamo in grado di chiarire l'uso impreciso del lvalue, rvalue ecc sopra.

citando cppreference per i posteri:

  • un lvalue è

    un'espressione che ha un'identità e non possono essere spostati da.

    Quindi, non c'è alcuna cosa come un oggetto lvalue, ma c'è un oggetto che viene localmente chiamato (o di cui) da un'espressione lvalue

  • un rvalue è

    un espressione che è o un valore o un valore x. È possibile spostare da. Può o non può avere identità.

    • un prvalue (rvalue puro) è più o meno l'espressione si riferisce a un oggetto temporaneo senza nome: non siamo in grado di convertire la nostra espressione lvalue a uno di questi IIUC.

    • un xValue (valore scadenza) è

      un'espressione che ha identità e può essere spostata da.

      che include esplicitamente il risultato di std::move

Così che effettivamente accade:

  • esiste un oggetto
  • l'oggetto è identificato localmente da un lvalue, da cui non è possibile spostare (per proteggerci dagli effetti collaterali imprevisti)
  • std::move cede un'espressione xValue (che può essere spostato da) riferimento allo stesso oggetto come espressione lvalue
  • significa oggetti come variabili (che prendono il nome da espressioni lvalue) non può essere implicitamente spostato da e deve invece esplicitamente spostato da tramite un'espressione xvalue esplicita come std::move.
  • provvisori anonimi sono probabilmente già indicati con espressioni prvalue, e possono essere spostati in modo implicito
+1

Quando si utilizza 'std :: move', non si sta vincolando un riferimento di rvalue a un lvalue. Stai lanciando il valore di lvalue a rvalue, quindi legando il riferimento rvalue a quello. –

+0

Non è l'espressione intermedia di xvalue? In entrambi i casi, il risultato finale è un riferimento di valore aliasing di un oggetto precedentemente identificato come lvalue ... – Useless

+0

@Useless Ma gli oggetti non hanno categorie di valori, le espressioni lo fanno. L'espressione 'object' è un lvalue, l'espressione' std :: move (oggetto) 'in un valore. – TartanLlama

3

In sostanza, un meccanismo è necessaria per distinguere tra i valori che possono essere spostati da, e quelli che non possono essere spostati da (vale a dire sarebbe necessaria una copia).

Consentire sia rvalues ​​che lvalue di essere associati a un riferimento lvalue rende impossibile.

Di conseguenza, è possibile spostare i valori associati a un riferimento di rval (non necessariamente sempre spostati da, ma è consentito) e gli lvalue possono essere associati a riferimenti lvalue e non possono essere spostati da.

std::move è lì per consentire il lancio tra le categorie di valori (a un valore) per consentire lo spostamento.

Nota; i riferimenti di constvalue (const T&) possono essere associati a entrambi i valori rvalue (temporaries) e lvalue poiché l'oggetto riferito all'oggetto non può essere modificato (è contrassegnato come const, quindi non può esserci alcun movimento da alcuno).

C'è un po 'di storia (agli albori del C++) sul perché gli oggetti temporanei non potevano essere associati a riferimenti non costanti per cominciare con ... il dettaglio è sfocato ma c'era qualche ragionamento che modificava un temporaneo didn't make sense, dato che sarebbe comunque distrutto alla fine della dichiarazione corrente. Inoltre potresti essere cullato nel senso che stavi modificando un lvalue, quando invece non lo facevi, la semantica del codice poteva/sarebbe sbagliata ed essere bacata.There are further reasons legato a indirizzi, letterali ecc. Questo era prima di muoversi e la sua semantica si solidificò, ed è una delle motivazioni per lo spostamento e la sua semantica.

+0

La mia impressione generale è che mentre cercavo di risolvere qualcosa, il comitato C++ ha creato un pasticcio più grande. La trama precedente o 'T &&' era possibile utilizzare riferimenti per modificare gli oggetti passati (non è importante che tipo di modifica avvenga). Ciò significa che in ogni situazione in cui viene passato un riferimento non const, deve essere verificato ciò che accade con l'entità passata. In realtà cercano di creare una nozione secondo cui 'T &' non dovrebbe essere modificato (vale a dire che il suo contenuto non dovrebbe essere preso). La nozione è scarsa. 'const T &' dovrebbe essere usato per quello. –

+0

L'effetto netto delle loro complesse regole sarà opposto. Verrà scritto un sacco di codice buggy perché molti programmatori invece di cercare di capire i minimi dettagli del design della lingua scriveranno 'qualcosa'' in qualche modo'. Sì, a volte il loro codice funzionerà. In altri casi, come al solito. –