Da der Standard aus welchen Gründen auch immer keinen konvertierenden Kopierkonstruktor wie folgt definiert:
// in class initializer_list
template<class U>
initializer_list(initializer_list<U> const& other);
Die initializer_list<rref_wrapper<move_only>> erstellt durch die Klammer-Init-Liste ({...}) wird nicht in konvertiert initializer_list<move_only> dass die vector<move_only> nimmt. Wir brauchen hier also eine zweistufige Initialisierung:
Ah … das ist das rvalue-Analogon von std::ref, nicht? Vielleicht sollte es heißen std::rref.
– Kerrek SB
12. Dezember 2011 um 1:39 Uhr
Nun, ich denke, das sollte nicht ohne Erwähnung in einem Kommentar bleiben 🙂 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));.
– Johannes Schaub – litb
12. Dezember 2011 um 22:18 Uhr
@Johannes: Manchmal sind es die einfachen Lösungen, die mir einfach entgehen. Obwohl ich zugeben muss, dass ich mich nicht darum gekümmert habe move_iteratorist noch.
– Xeo
12. Dezember 2011 um 22:37 Uhr
@Johannes: Warum ist das keine Antwort? 🙂
– Xeo
12. Dezember 2011 um 22:43 Uhr
@JohanLundberg: Ich würde das als QoI-Problem betrachten, aber ich verstehe nicht, warum es das nicht tun könnte. Die stdlib von VC++ zum Beispiel Tag-Dispatches basierend auf der Iterator-Kategorie und -Verwendung std::distance für Forward-or-better-Iteratoren und std::move_iterator passt die Kategorie des zugrunde liegenden Iterators an. Wie auch immer, gute und prägnante Lösung. Vielleicht als Antwort posten?
– Xeo
2. November 2012 um 22:49 Uhr
Die Synopse von <initializer_list> in 18.9 macht einigermaßen klar, dass Elemente einer Initialisiererliste immer per const-Referenz übergeben werden. Leider scheint es in der aktuellen Version der Sprache keine Möglichkeit zu geben, move-semantic in Initialisierungslistenelementen zu verwenden.
Konkret haben wir:
typedef const E& reference;
typedef const E& const_reference;
typedef const E* iterator;
typedef const E* const_iterator;
const E* begin() const noexcept; // first element
const E* end() const noexcept; // one past the last element
Betrachten Sie das Idiom in, das auf cpptruths (cpptruths.blogspot.com/2013/09/…). Die Idee ist, lvalue/rvalue zur Laufzeit zu bestimmen und dann move oder copy-construction aufzurufen. in erkennt rvalue/lvalue, obwohl die von initializer_list bereitgestellte Standardschnittstelle eine const-Referenz ist.
– Sumant
24. September 2013 um 19:34 Uhr
@Sumant Kommt mir nicht so “idiomatisch” vor: Ist es nicht stattdessen reines UB? wie es nicht nur der Iterator, sondern auch die zugrunde liegenden Elemente selbst sein könnten constdie in einem wohlgeformten Programm nicht weggeworfen werden können.
– Unterstrich_d
18. Juli 2016 um 10:49 Uhr
Wie in anderen Antworten erwähnt, ist das Verhalten von std::initializer_list Ist es, Objekte nach Wert zu halten und nicht ausziehen zu lassen, so ist dies nicht möglich. Hier ist eine mögliche Problemumgehung mit einem Funktionsaufruf, bei dem die Initialisierer als variadische Argumente angegeben werden:
Unglücklicherweise multi_emplace(foos, {}); schlägt fehl, da der Typ für nicht abgeleitet werden kann {}, also müssen Sie für Objekte, die standardmäßig erstellt werden sollen, den Klassennamen wiederholen. (oder verwenden vector::resize)
Die rekursive Paketerweiterung könnte durch den Dummy-Array-Kommaoperator hack ersetzt werden, um ein paar Codezeilen einzusparen
– MM
10. November 2015 um 1:12 Uhr
Metall
Update für C++20: Mit dem Trick von Johannes Schaub std::make_move_iterator() mit C++20 std::to_array()können Sie eine Hilfsfunktion wie unto verwenden make_tuple() usw., hier genannt make_vector():
#include <array>
#include <memory>
#include <vector>
struct X {};
template<class T, std::size_t N>
auto make_vector( std::array<T,N>&& a )
-> std::vector<T>
{
return { std::make_move_iterator(std::begin(a)), std::make_move_iterator(std::end(a)) };
}
template<class... T>
auto make_vector( T&& ... t )
{
return make_vector( std::to_array({ std::forward<T>
}
int main()
{
using UX = std::unique_ptr<X>;
const auto a = std::to_array({ UX{}, UX{}, UX{} }); // Ok
const auto v0 = make_vector( UX{}, UX{}, UX{} ); // Ok
//const auto v2 = std::vector< UX >{ UX{}, UX{}, UX{} }; // !! Error !!
}
Vielleicht kann jemand hebeln std::make_array()‘s Trickserei zuzulassen make_vector() sein Ding direkt zu machen, aber ich sah nicht wie (genauer gesagt, ich versuchte, was meiner Meinung nach funktionieren sollte, scheiterte und ging weiter). In jedem Fall sollte der Compiler in der Lage sein, die Array-zu-Vektor-Transformation zu inlinen, wie es Clang mit O2 tut GodBolt.
Wie bereits erwähnt, ist es nicht möglich, einen Vektor vom Nur-Verschieben-Typ mit einer Initialisiererliste zu initialisieren. Die ursprünglich von @Johannes vorgeschlagene Lösung funktioniert gut, 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 dieses Array bereits anstelle des Speicherblocks des Vektors zu initialisieren?
Hier ist meine Funktion zum Initialisieren eines Vektors von unique_ptrverwendet ein Argumentpaket:
#include <iostream>
#include <vector>
#include <make_unique.h> /// @see http://stackoverflow.com/questions/7038357/make-unique-and-perfect-forwarding
template <typename T, typename... Items>
inline std::vector<std::unique_ptr<T>> make_vector_of_unique(Items&&... items) {
typedef std::unique_ptr<T> value_type;
// Allocate memory for all items
std::vector<value_type> result(sizeof...(Items));
// Initialize the array in place of allocated memory
new (result.data()) value_type[sizeof...(Items)] {
make_unique<typename std::remove_reference<Items>::type>(std::forward<Items>(items))...
};
return result;
}
int main(int, char**)
{
auto testVector = make_vector_of_unique<int>(1,2,3);
for (auto const &item : testVector) {
std::cout << *item << std::endl;
}
}
Das ist eine schreckliche Idee. Placement new ist kein Hammer, es ist ein Werkzeug feiner Präzision. result.data() ist kein Zeiger auf irgendeinen Zufallsspeicher. Es ist ein Zeiger auf ein Objekt. Denken Sie daran, was mit diesem armen Objekt passiert, wenn Sie es neu platzieren.
– R.Martinho Fernandes
15. Mai 2013 um 11:38 Uhr
Außerdem ist die Array-Form der Platzierung neu nicht wirklich verwendbar. stackoverflow.com/questions/8720425/…
– R.Martinho Fernandes
15. Mai 2013 um 11:42 Uhr
@R. Martinho Fernandes: Danke für den Hinweis, dass die Platzierung neu für Arrays nicht funktionieren würde. Jetzt verstehe ich, warum das eine schlechte Idee war.
– Gart
15. Mai 2013 um 11:55 Uhr
Das ist eine schreckliche Idee. Placement new ist kein Hammer, es ist ein Werkzeug feiner Präzision. result.data() ist kein Zeiger auf irgendeinen Zufallsspeicher. Es ist ein Zeiger auf ein Objekt. Denken Sie daran, was mit diesem armen Objekt passiert, wenn Sie es neu platzieren.
– R.Martinho Fernandes
15. Mai 2013 um 11:38 Uhr
Außerdem ist die Array-Form der Platzierung neu nicht wirklich verwendbar. stackoverflow.com/questions/8720425/…
– R.Martinho Fernandes
15. Mai 2013 um 11:42 Uhr
@R. Martinho Fernandes: Danke für den Hinweis, dass die Platzierung neu für Arrays nicht funktionieren würde. Jetzt verstehe ich, warum das eine schlechte Idee war.
– Gart
15. Mai 2013 um 11:55 Uhr
9926400cookie-checkKann ich einen Vektor vom Typ „Nur verschieben“ listeninitialisieren?yes
This website is using cookies to improve the user-friendliness. You agree by using the website further.
Visual Studio und clang haben das gleiche Verhalten
– Jean-Simon Brochu
12. März 2018 um 15:53 Uhr