2010-10-06 2 views
8

Dopo lo spostamento di un oggetto, deve essere distruttibile:Come possono essere usati gli oggetti spostati?

T obj; 
func(std::move(obj)); 
// don't use obj and let it be destroyed as normal 

Ma cos'altro può essere fatto con obj? Potresti spostare un altro oggetto in esso?

T obj; 
func(std::move(obj)); 
obj = std::move(other); 

Questo dipendono dal tipo esatto? (Ad esempio, std :: vector potrebbe fornire garanzie specifiche per le quali non è possibile fare affidamento per tutti i T.) È necessario o persino ragionevole che tutti i tipi supportino qualcosa oltre alla distruzione sugli oggetti spostati da?

risposta

6

Sì, è possibile spostare un altro oggetto in esso. std :: swap fa questo.

+1

Avevo dimenticato std: swap, buon punto. –

+0

Sembra essere l'unico requisito ragionevole, oltre alla distruzione, per qualsiasi tipo. Prenderò nota che l'operatore di assegnazione del movimento potrebbe essere cancellato (producendo quindi un errore in fase di compilazione) e che lo scambio richiede MoveAssignable (§20.3.2p1, 0x FCD) per rendere questo lavoro-implicando alcuni tipi, che non possono essere usato con lo scambio, * non * MoveAssignable. –

+0

E sembra che \ _move \ _assign (§20.7.4.3, tabella 45, 0x FCD) indichi se MoveAssignable è soddisfatto. –

1

Questa è la semantica del tipo. Tu decidi. Spetta a te come implementare la mossa.

In generale, lo stato deve essere uguale a quello ottenuto utilizzando il costruttore non parametrico.

Btw. move ha senso solo se si sta memorizzando un blocco dati dietro un puntatore (o qualche altra classe mobile).

+0

Chiedo all'utente di questi tipi, non come implementatore. "Se scrivo una funzione basata su T, quali operazioni so per qualsiasi T?" T può essere qualsiasi cosa, da int a std :: vector a, beh, * qualunque * altro. –

+0

@Roger: se si scrive un modello di funzione, sono richiesti i tipi che possono essere passati. Se il tipo non soddisfa i requisiti utilizzati in modo errato. –

+0

@Let: Ecco perché sto chiedendo quali sono i requisiti garantiti per qualsiasi tipo. Ad esempio, vedi le risposte che indicano che gli oggetti spostati da (possono essere spostati) in ("spostati-assegnati"). –

1

Dipende dal codice classe. Se la classe non ha un costruttore di riferimento di rvalue e un operatore di assegnazione, std :: move viene ignorato. std :: move non sposta nulla, consente solo di trattare il suo argomento come riferimento di rvalue, se è disponibile la funzione appropriata.

Scritto correttamente & & costruttore e operatore = deve lasciare l'istanza del parametro in uno stato coerente, come una stringa vuota, e l'oggetto deve essere utilizzabile. Se c'è operatore =, un altro oggetto può essere assegnato correttamente a tale istanza vuota.

Modifica.

Generalmente, std :: mossa dovrebbe essere utilizzato per applicare semantica di movimento verso variabile non rvalue, ma in realtà è:

 
SomeClass::SomeClass(SomeClass&& v) 
{ 
    // Inside of this function, v is not rvalue anymore. But I know that actually 
    // this is rvalue, and use std::move 
    OtherFunction(std::move(v)); 
} 

In questo caso, il requisito mininal a v è che dovrebbe essere in grado morire senza problemi.

Quando std :: move viene utilizzato per la variabile che non è effettivamente un riferimento di valore, in realtà, questa variabile di utilizzabilità potrebbe non essere definita. Per le mie classi personali, garantirei una certa coerenza per questo caso. Per altre classi, dipende dall'implementazione specifica della classe, ma non applicherei std :: move agli oggetti che non sono effettivamente riferimenti di valore. Non so davvero come questo sia definito (e se sia definito) nello standard.

+0

Sto chiedendo quali sono i requisiti esatti per quello stato coerente. Il modo in cui capisco le cose al momento, dopo func (std :: move (obj)), non è garantito che obj.method() possa essere chiamato, a meno che tu non sappia esattamente cosa T sia e sappia che garantisce che tutto sia a posto. –

+0

@Roger Ovviamente è possibile chiamare 'obj.method();' L'oggetto non va da nessuna parte. Ha appena cambiato stato. –

+0

@Let: Con "non è garantito che obj.method() possa essere chiamato", voglio dire "non sei garantito che obj.method() non sia UB". Mi rendo conto che la sintassi è ben strutturata. –

5

L'attuale bozza di C++ 0x richiede che un oggetto spostato da possa essere distrutto o assegnato a. Se si passa l'oggetto a una funzione nella libreria standard, è tutto ciò che si assume.

Generalmente è considerata una buona pratica garantire che un oggetto spostato da un oggetto "funzionante" del suo tipo soddisfi tutti gli invarianti. Tuttavia, è in uno stato non specificato --- se si tratta di un contenitore, non si conosce quanti elementi ha o che cosa sono, ma si dovrebbe essere in grado di chiamare size() e empty() e interrogarlo.

La bozza corrente non è chiara su ciò che è richiesto dai tipi di libreria standard stessi, e al riguardo c'è una discussione attiva nel comitato del C++.

+0

Potrebbe essere una buona idea permettere agli utenti di chiamare un metodo size(), ma se scrivo il mio contenitore, sono autorizzato a rendere obj.size() come UB se obj è stato spostato? –

+0

Sì, con il tuo tipo puoi fare quello che ti piace. Consiglio vivamente di aggiungere una query 'valid()' se lo fai, dato che gli stati non rilevabili che "esplodono" quando usati sono un vero problema da affrontare. –

+1

Non direi che ogni classe abilitata al movimento debba supportare l'assegnazione. Ma se la classe supporta alcuni tipi di incarichi questo/questi dovrebbe essere ancora utilizzabile in uno stato "trasferito da". – sellibitze