2016-07-23 10 views
-1

Schrieb den folgenden Code in VS13:Warum std :: bewegen sich nicht bewegte

std::vector<std::string> myvector(1000); 
std::fill(myvector.begin(), myvector.end(), "Hello World"); 
std::vector<std::string> pushto; 
for (auto s: myvector) 
    pushto.push_back(std::move(*s)); 

Works bewegte sich aber nicht, es Zeichenfolge kopieren ctor statt genannt. myvector hatte am Ende noch seine "Hello World" s. Verwenden von regulären C++ 98 Iteration wie folgt aus:

std::vector<std::string> myvector(1000); 
std::fill(myvector.begin(), myvector.end(), "Hello World"); 
std::vector<std::string> pushto; 
for (auto s = myvector.begin(); s != myvector.end();s++) 
    pushto.push_back(std::move(*s)); 

Eigentlich gearbeitet und Bewegung genannt wurde. myvector Saiten waren leer. Warum funktioniert die erste moderne Notation nicht?

+4

Kompiliert das erste Beispiel wirklich? Ich denke nicht, dass es das '*' in 'std :: move (* s)' geben sollte. – aschepler

+0

Beachten Sie, dass niemand gesagt hat, dass ein Zug das Original löschen sollte. Nach dem Verschieben befindet sich das Original in einem * gültigen aber nicht spezifizierten * Zustand. Selbst wenn Sie das '&' an Ort und Stelle setzen, ist es vollkommen gültig (und, imho, manchmal gewünscht), dass das ursprüngliche Objekt dieselben Werte wie das neue hat (d. H. Wie zuvor, bis modifiziert). – lorro

Antwort

5

In Ihrem ersten Beispiel verwenden Sie for (auto s: myvector), s In diesem Fall ist eine Kopie des Werts in der aktuellen Iteration. um zu erreichen, was Sie wollen, sollten Sie es als Referenz tun - for (auto& s: myvector).

Beachten Sie, dass die Zeichenfolge nach std::move nicht garantiert geleert wird. Dieser Aufruf gibt sein Argument nur an eine rvalue-Referenz (&&) aus. andere Funktionen (wie std::vector::push_back), die eine Überladung für Rvalue Reference Argumente möglicherweise ihre argumnet Ressourcen freigeben.

+2

.. und selbst dann ist es nicht garantiert, dass die alte Saite leer ist. – lorro

+0

In der Tat, aber dies ist der richtige Weg, um 'std :: move' zu ​​verwenden (und es wird wahrscheinlich im Compiler des OP geleert, da es im zweiten Beispiel geleert wurde) – Xiobiq

+0

Richtig: definitiv stimme ich Ihnen zu. Mit Ausnahme von Leaning: das ist das Implementierungsdetail der Klasse * und * des Compilers (beides auch nicht). – lorro

3

Wie @Polikdir sagte, kommt das Kopieren von der Kopie, die Sie bei (auto s: myvector) gemacht haben. Eine Möglichkeit ist es, die Bereich-for-Schleife zu verwenden, mit && (Forwarding Referenz) oder & (normal Referenz):

for (auto & val : myvector) 
    pushto.push_back(std::move(val)); 

nicht bekannt, aber es gibt einen speziellen Algorithmus für die Objekte zwischen den Behältern zu bewegen. Es heißt eigentlich auch std::move.

std::move(s.begin(), s.end(), std::back_inserter(pushto)); 

edit:

q: Da std :: nur wirft auf rvalue Bezug bewegen wird wirklich benötigt? Ist nicht std :: move in diesem Fall einfach redundant? Nein, weil eine Variable (z. B. val) keine r-Wert-Referenz sein kann. Deshalb müssen wir std::forward<T> auf universellen Referenzen aufrufen.

Hinweis: What does `auto && e` do in range-based for-loops?

+0

Da 'std :: move' nur in rvalue referenziert, ist es wirklich nötig? Ist 'std :: move 'in diesem Fall nicht einfach redundant? – Xiobiq

+1

3-Param 'move', guter Hinweis. – lorro

+0

Vielen Dank, sehr hilfreiche Lösung – orenshochat

Verwandte Themen