Wenn ich den folgenden Code durch meinen GCC 4.7-Snapshot übergebe, wird versucht, das unique_ptr
s in den Vektor zu kopieren .
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
Offensichtlich kann das nicht funktionieren, weil std::unique_ptr
es nicht kopierbar ist:
Fehler: Verwendung der gelöschten Funktion 'std :: unique_ptr <_Tp, _Dp> :: unique_ptr (const std :: unique_ptr <_Tp, _Dp> &) [mit _Tp = int; _Dp = std :: default_delete; std :: unique_ptr <_Tp, _Dp> = std :: unique_ptr] '
Ist GCC korrekt beim Versuch, die Zeiger aus der Initialisierungsliste zu kopieren?
c++
c++11
initializer-list
move-semantics
R. Martinho Fernandes
quelle
quelle
Antworten:
Die Zusammenfassung von
<initializer_list>
in 18.9 macht ziemlich deutlich, dass Elemente einer Initialisiererliste immer über const-reference übergeben werden. Leider scheint es in der aktuellen Version der Sprache keine Möglichkeit zu geben, die Bewegungssemantik in Initialisierungslistenelementen zu verwenden.Insbesondere haben wir:
quelle
const
, die in einem wohlgeformten Programm nicht weggeworfen werden können.Bearbeiten: Da @Johannes nicht die beste Lösung als Antwort veröffentlichen möchte, mache ich es einfach.
Die von zurückgegebenen Iteratoren
std::make_move_iterator
verschieben das Element, auf das verwiesen wird, wenn sie dereferenziert werden.Ursprüngliche Antwort: Wir werden hier einen kleinen Hilfstyp verwenden:
Leider funktioniert der einfache Code hier nicht:
Da der Standard aus irgendeinem Grund keinen konvertierenden Kopierkonstruktor wie diesen definiert:
Das
initializer_list<rref_wrapper<move_only>>
von der Klammer-init-Liste ({...}
) erstellte wird nicht in das konvertiert,initializer_list<move_only>
was dasvector<move_only>
nimmt. Wir brauchen hier also eine zweistufige Initialisierung:quelle
std::ref
, nicht? Vielleicht sollte es genannt werdenstd::rref
.move_only m[] = { move_only(), move_only(), move_only() }; std::vector<move_only> v(std::make_move_iterator(m), std::make_move_iterator(m + 3));
.move_iterator
gekümmert.std::distance
für Forward-or- Better -Iteratoren verwendet werden,std::move_iterator
passt die zugrunde liegende Iteratorkategorie an. Wie auch immer, gute und prägnante Lösung. Vielleicht als Antwort posten?Wie in anderen Antworten erwähnt, besteht das Verhalten von
std::initializer_list
darin, Objekte nach Wert zu halten und kein Herausziehen zuzulassen, sodass dies nicht möglich ist. Hier ist eine mögliche Problemumgehung mithilfe eines Funktionsaufrufs, bei dem die Initialisierer als verschiedene Argumente angegeben werden:Unglücklicherweise
multi_emplace(foos, {});
schlägt dies fehl, da der Typ für nicht abgeleitet werden{}
kann. Damit Objekte standardmäßig erstellt werden, müssen Sie den Klassennamen wiederholen. (oder verwendenvector::resize
)quelle
Mit Johannes Schaubs Trick von
std::make_move_iterator()
mitstd::experimental::make_array()
können Sie eine Hilfsfunktion verwenden:Sehen Sie es live weiter Coliru.
Vielleicht kann jemand den
std::make_array()
Trick nutzen, ummake_vector()
seine Sache direkt erledigen zu können, aber ich habe nicht gesehen, wie (genauer gesagt, ich habe versucht, was meiner Meinung nach funktionieren sollte, bin gescheitert und bin weitergegangen). In jedem Fall sollte der Compiler in der Lage sein, das Array in eine Vektortransformation zu integrieren, wie dies Clang bei eingeschaltetem O2 tut GodBolt.quelle
Wie bereits erwähnt, ist es nicht möglich, einen Vektor vom Typ "Nur Verschieben" mit einer Initialisierungsliste zu initialisieren. Die ursprünglich von @Johannes vorgeschlagene Lösung funktioniert einwandfrei, aber ich habe eine andere Idee ... Was ist, wenn wir kein temporäres Array erstellen und dann Elemente von dort in den Vektor verschieben, sondern die Platzierung verwenden
new
, um dieses Array bereits anstelle des zu initialisieren Vektorspeicherblock?Hier ist meine Funktion zum Initialisieren eines Vektors von
unique_ptr
's mithilfe eines Argumentpakets:quelle
result.data()
ist kein Zeiger auf einen zufälligen Speicher. Es ist ein Zeiger auf ein Objekt . Denken Sie daran, was mit diesem armen Objekt passiert, wenn Sie es neu platzieren.