2010-01-26 10 views
17

In C++ 11, si può ottenere una spinta efficienza utilizzando std::move quando vogliamo spostare (distruttivamente copiare) valori in un contenitore:C++ "mossa da" contenitore

SomeExpensiveType x = /* ... */; 
vec.push_back(std::move(x)); 

ma non posso trova qualcosa che va dall'altra parte Quello che voglio dire è qualcosa di simile:

SomeExpensiveType x = vec.back(); // copy! 
vec.pop_back(); // argh 

Questo è più frequente (la copia-pop) su adattatore di come stack. Può esistere qualcosa del genere:

Per evitare una copia? E questo esiste già? Non ho trovato nulla di simile in n3000.

Ho la sensazione che mi manca qualcosa dolorosamente ovvio (come l'inutilità di esso), quindi sono pronto per "ru dum". : 3

+0

Quali implicazioni ha il metodo di spostamento sulle variabili che non rientrano nell'ambito di applicazione? Ad esempio, se creo un oggetto, lo aggiungo a un contenitore membro, quindi l'oggetto esce dall'ambito ... Poiché non è stata eseguita alcuna copia, l'oggetto nel contenitore membro è ancora definito? – Polaris878

+0

Sì, è stato spostato nel contenitore. Ti consigliamo di consultare i riferimenti di Google rvalue se non sei sicuro di come funzionano. – GManNickG

+0

dolce grazie ... dovrò scavare in questo ancora un po 'di lol. Al valore nominale sembra che ciò potrebbe causare problemi. – Polaris878

risposta

15

Potrei sbagliarmi totale qui, ma non è quello che si vuole solo

SomeExpensiveType x = std::move(vec.back()); vec.pop_back(); 

Supponendo SomeExpensiveType ha un costruttore mossa. (E ovviamente vero per il vostro caso)

+0

Ci ho pensato, ma sono cauto nello spostare qualcosa dal container in quel modo. Ora che ci penso di più, sembra perfettamente legale ...: | – GManNickG

+0

Hmm. Non sono sicuro che sia corretto. Sicuramente vec.back() dovrebbe avere la semantica del movimento, cioè restituire un && – ScaryAardvark

+0

Attenzione, ci ho pensato un po 'di più e potresti avere ragione. – ScaryAardvark

-1

In generale per i tipi di costosi penso che ci si vuole spingere sia una classe wrapper o puntatore intelligente nel contenitore, invece. In questo modo si evitano copie costose e invece si eseguono solo le copie economiche del puntatore intelligente o della classe wrapper. Puoi anche usare i puntatori grezzi anche se vuoi haha.

class ExpensiveWrapper 
{ 
public: 
    ExpensiveWrapper(ExpensiveClass* in) { mPtr = in; } 

    // copy constructors here.... 

private: 
    ExpensiveWrapper* mPtr; 

}; 
+3

Un punto della semantica 'move' è di eliminare questo metodo. I contenitori "spostano" il loro contenuto solo invece di copiarli. – GManNickG

4

Per completezza (e chiunque inciampare su questa domanda senza un compilatore C++ 1x), un'alternativa che esiste già oggi:

SomeExpensiveType x; 
std::swap(x, vec.back()); 
vec.pop_back(); 

Si richiede solo una specializzazione di std::swap ad esistere in un tipo di elemento.

+3

Tranne non utilizzare std :: swap esplicitamente (in quanto non può essere specializzato in modo corretto per molti tipi); utilizzare una dichiarazione di utilizzo con una chiamata di scambio non qualificata. –

+1

@ Poger Pate - o usare 'boost :: swap' per ottenere la stessa cosa. –

+0

Preferisco scrivere una sola riga piuttosto che preoccuparsi di chi si lamenterà di Boost, come inevitabilmente accade a qualcuno. :) –

4
template<class C> 
auto pop_back(C& c) -> typename std::decay<decltype(c.back())>::type 
{ 
    auto value (std::move(c.back())); 
    c.pop_back(); 
    return value; // also uses move semantics, implicitly 
    // RVO still applies to reduce the two moves to one 
} 
+0

Trovo che std :: decay sia più appropriato di std :: remove_reference. Ad esempio, trasforma "const MyClass &" in "MyClass" (rimuovendo const). – sellibitze

+0

@sellibitze: hai ragione, è più appropriato; Grazie! –

+0

Non sono sicuro al 100% ma un 'typename' è probabilmente mancante dopo' -> ' – sellibitze

Problemi correlati