Riutilizzare un contenitore spostato?


85

Qual è il modo corretto per riutilizzare un contenitore spostato?

std::vector<int> container;
container.push_back(1);
auto container2 = std::move(container);

// ver1: Do nothing
//container2.clear(); // ver2: "Reset"
container = std::vector<int>() // ver3: Reinitialize

container.push_back(2);
assert(container.size() == 1 && container.front() == 2);

Da quello che ho letto nella bozza standard C ++ 0x; ver3 sembra essere il modo corretto, poiché un oggetto dopo il movimento è in un file

"Se non diversamente specificato, tali oggetti spostati da devono essere posti in uno stato valido ma non specificato."

Non ho mai trovato alcuna istanza in cui è "altrimenti specificato".

Anche se trovo ver3 un po 'indiretto e avrei preferito ver1, sebbene vec3 possa consentire alcune ottimizzazioni aggiuntive, ma d'altra parte può facilmente portare a errori.

La mia ipotesi è corretta?


4
Potresti semplicemente chiamare clear, poiché non ha precondizioni (e quindi nessuna dipendenza dallo stato dell'oggetto).
Nicol Bolas

@Nicol: diciamo che c'era std::vectorun'implementazione che memorizzava un puntatore alla sua dimensione (sembra sciocco, ma legale). Lo spostamento da quel vettore potrebbe lasciare il puntatore NULL, dopodiché clearfallirebbe. operator=potrebbe anche fallire.
Ben Voigt

10
@ Ben: penso che violerebbe la parte "valida" di "valido ma non specificato".
ildjarn

1
@ildjarn: pensavo volesse solo dire che è sicuro eseguire il distruttore.
Ben Voigt

Immagino che la domanda sia: cosa è "valido"?
Ronag

Risposte:


98

Dalla sezione 17.3.26 delle specifiche "stato valido ma non specificato":

uno stato dell'oggetto che non è specificato eccetto che le invarianti dell'oggetto sono soddisfatte e le operazioni sull'oggetto si comportano come specificato per il suo tipo [Esempio: Se un oggetto xdi tipo std::vector<int>è in uno stato valido ma non specificato, x.empty()può essere chiamato incondizionatamente e x.front()può essere chiamato solo se x.empty()restituisce falso. —End esempio]

Pertanto, l'oggetto è vivo. È possibile eseguire qualsiasi operazione che non richieda una precondizione (a meno che non si verifichi prima la precondizione).

clear, ad esempio, non ha precondizioni. E restituirà l'oggetto a uno stato noto. Quindi cancellalo e usalo normalmente.


Dove posso leggere nello standard le "precondizioni" per es. Metodi std :: vector?
Ronag

1
@ronag: §23.2 contiene le tabelle in cui sono elencate.
Grizzly

2
Ho trovato il seguente che è interessante, open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html , scrivono "i contenitori possono essere 'più vuoti che vuoti'".
Ronag

4
@ronag: 1) Se il contenitore è in uno stato valido, la chiamata clearè valida. 2) Mentre il contenitore era in uno stato non specificato, la chiamata clearmette il contenitore in uno stato specificato perché ha delle postcondizioni obbligatorie nello standard (§23.2.3 tabella 100). std::vector<T>ha una classe invariante che push_back()è sempre valida (fintanto che Tè CopyInsertable).
ildjarn

3
@ronag: open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3241.html stava citando uno dei commenti dell'ente nazionale sulla citazione "più vuoto del vuoto". Il commento dell'ente nazionale non era corretto. N3241 non ha proposto un tale stato. Se un'implementazione di uno std :: container ha uno stato "più vuoto che vuoto" risultante da uno spostamento da, allora quello stato deve essere uno stato valido (cioè puoi fare qualsiasi cosa con quell'oggetto che non richiede precondizioni).
Howard Hinnant

11

L'oggetto si trova in uno stato valido, ma indefinito significa fondamentalmente che mentre lo stato esatto dell'oggetto non è garantito, è valido e poiché tali funzioni membro (o funzioni non membro) sono garantite per funzionare fintanto che non fanno affidamento sull'oggetto avente un certo stato.

La clear()funzione membro non ha precondizioni sullo stato dell'oggetto (a parte il fatto che è valido, ovviamente) e può quindi essere chiamata su oggetti spostati da. D'altra parte, ad esempio, front()dipende dal fatto che il contenitore non sia vuoto, e quindi non può essere chiamato, poiché non è garantito che non sia vuoto.

Pertanto sia la ver2 che la ver3 dovrebbero andare bene.


Un vettore sarà sempre vuoto, ma questo non è vero per il caso generale, (IE array)
Mooing Duck

"Un vettore sarà sempre vuoto", su cosa lo basi?
Ronag

1
@ronag: intendevo ver2 e ver3 ovviamente (come dovrebbe essere chiaro dal testo, corretto l'errore di battitura
Grizzly

È interessante notare che le condizioni preliminari per front()sono indicate solo per std::array, e anche lì non nella tabella.
Ben Voigt

1
@ Ben: §23.2.3 tabella 100 dice che la semantica operazionale di front()are *a.begin(), §23.2.1 / 6 dice " Se il contenitore è vuoto, allorabegin() == end() ", e §24.2.1 / 5 dice " La libreria non assume mai che passato- i valori finali sono dereferenziabili. ". Di conseguenza penso che i presupposti per si front()possano dedurre, anche se potrebbe certamente essere reso più chiaro.
ildjarn

-8

Non penso che tu possa fare NIENTE con un oggetto spostato da (tranne distruggerlo).

Non puoi usare swapinvece, per ottenere tutti i vantaggi dello spostamento ma lasciare il container in uno stato noto?


+1. swap è una buona idea, anche se non funzionerà in tutti i casi, ad esempio l'uso di auto non funzionerà. Forse un safe_move, che utilizza lo swap internamente, potrebbe essere un'idea?
Ronag

5
È un oggetto attivo e puoi utilizzare qualsiasi funzione che non abbia precondizioni (a parte gli invarianti)
Mooing Duck

Il modello principale per std::swapha 2 assegnazioni di spostamento, con le destinazioni di tali assegnazioni che vengono spostate dai valori. Questo conta come "fare qualcosa a un oggetto spostato" per me
Caleth
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.