emplace_back



  • Emplace_back versucht ja, Objekte im Container in-place zu konstruieren, statt wie bei push_back die Elemente in den Container zu kopieren, sei es durch das Kopieren eines bestehenden Objekts oder aber der zwischenzeitlichen Erstellung eines temporären Objekts, was dann anschließend kopiert wird.
    Da aber ja viele Klassen einen copy-Konstruktor haben, frage ich mich, ob emplace_back nicht immer die bessere Wahl ist, denn auch ein bestehendes Objekt passt doch in den copy-ctor und erfüllt damit das kriterium für emplacements oder verstehe ich da etwas falsch?

    std::vector<QImage> qImages_;
    QImage img{};
    qImages_.emplace_back(img); // called   QImage(const QImage&);
    

    zudem erleichtert es beispielsweise bei maps das einfügen von elementen

    map.insert({key-type, mapped-type})
    
    map.emplace(key-type, mapped-type)
    


  • Sewing schrieb:

    frage ich mich, ob emplace_back nicht immer die bessere Wahl ist, denn auch ein bestehendes Objekt passt doch in den copy-ctor und erfüllt damit das kriterium für emplacements oder verstehe ich da etwas falsch?

    Hab gerade mal geschaut. Komme auch zu dem Entschluss, dass ich immer emplace_back verwenden kann.

    #include <utility>
    #include <string>
    #include <vector>
    using namespace std;
    
    int main()
    {
    	vector<string> vec;
    
    	// 1. Wenn emplace_back mit einem Argument aufgerufen wird (egal ob rvalue oder lvalue)
    	// machen emplace_back und push_back das gleiche.
    	string str;
    	vec.push_back(str); // copy construction
    	vec.emplace_back(str); // copy construction
    
    	vec.push_back( std::move(str) ); // in-place construction
    	vec.emplace_back( std::move(str) ); // in-place construction
    
    	// 2. Für jedes andere Arguement (oder Argumente), ist emplace_back besser als push_back.
    	vec.emplace_back( "foo" ); // ruft string( const char* ) auf für in-place construction.
    	vec.push_back( "foo" ); // ruft string( const char* ) auf + move(tmp) == vec.push_back( string("foo") )
    }
    


  • Nur ruft man mit emplace immer irgendwelche Konstruktoren auf. Wenn man einen vector<unique_ptr<T>> hat, dann kann man dem emplace irgendwelche T-Pointer geben und der unique_ptr im vector wird Owner, während das push_back nicht implizit im Argument schon aus einem T*-Pointer einen unique_ptr<T> erzeugt. Weil man also breiter konstruieren kann, muss man bei emplace mehr aufpassen. Also wenn du Code modernisierst und einen vector<owningRaw*> auf vector<unique_ptr<...>> umstellen willst: besonders aufpassen!



  • Das ist wohl auch so, im Meyers wird allerdings noch erwähnt, dass man durch emplace etwas Typsicherheit verliert (z.B. explicit ctor kompilert bei emplace (weil Argument für ctor) failed aber bei push_back (weil halt explicit).
    Scheint mir aber etwas konstruiert, der 'Nachteil'.



  • vor dem Hintergrund frage ich mich, wieso es keine iterator-adapter gibt wie

    back_emplacer ; )


  • Mod

    Sewing schrieb:

    vor dem Hintergrund frage ich mich, wieso es keine iterator-adapter gibt wie

    back_emplacer ; )

    Falls Frage ernst gemeint: Es gibt keinen technischen Grund, wieso man so etwas nicht machen könnte. Jedoch unhandlich in der Nutzung. Was weist du einem solchen Iterator zu? So ein Konstruktor wird in aller Regel schließlich mehr als nur ein Argument haben. Also entweder wäre so ein allgemeiner back_emplacer sehr unhandlich zu benutzen oder sehr beschränkt in der Nutzbarkeit. Wenn man unbedingt einen braucht, kann man sich immer noch einen programmieren.

    Außerdem ist der Unterschied zwischen push_back und emplace_back in der Praxis ein rein syntaktischer. Da die Container allesamt Templates sind, liegen beide Methoden dem Compiler bei der Benutzung im Klartext vor. Eine Kopie eines ansonsten unbenutzten Wertes zu einer Konstruktion direkt am Zielort zu optimieren gehört zur Grundausstattung eines jeden Optimierers.



  • Sewing schrieb:

    Emplace_back versucht ja, Objekte im Container in-place zu konstruieren, statt wie bei push_back die Elemente in den Container zu kopieren, sei es durch das Kopieren eines bestehenden Objekts oder aber der zwischenzeitlichen Erstellung eines temporären Objekts, was dann anschließend kopiert wird.
    Da aber ja viele Klassen einen copy-Konstruktor haben, frage ich mich, ob emplace_back nicht immer die bessere Wahl ist, denn auch ein bestehendes Objekt passt doch in den copy-ctor und erfüllt damit das kriterium für emplacements oder verstehe ich da etwas falsch?

    Nö, Du verstehst das schon richtig. Eine Sache kann push_back aber noch, die mit emplace nicht geht. Weil push_back einen konkreten Parametertypen hat (und da nix deduziert werden muss), kann man bei push_back den Parameter über eine Liste initialisieren:

    vector<vector<int>> v;
     v.push_back   ({23,42,99}); // OK
     v.emplace_back({23,42,99}); // Klappt nicht!
    

    Sowas ähnliches tauchte mal in einem Vortrag von Scott Meyers zum Thema "perfect forwarding" auf, was anscheinend ja nicht so "perfect" ist. 🙂



  • v.emplace_back(initializer_list<int>{23,42,99});
    


  • Wieso nicht einfach
    v.emplace_back(23,42,99); 😕 ?

    wob schrieb:

    v.emplace_back(initializer_list<int>{23,42,99});
    

    Was soll das bewirken?


  • Mod

    hustbaer schrieb:

    Wieso nicht einfach
    v.emplace_back(23,42,99); 😕 ?

    Wie sollst du damit einen vector initialisieren?

    Tatsächlich gibt es aber ein Proposal, dass Container bei emplace_back ---genauer via der construct Methode des Allokators---auch eine braced-init-list probieren soll. Bin gerade zu faul, das zu suchen.



  • Upps. Hab übersehen dass es um nen vector<vector> geht 🙂
    Dachte es wäre ein vector<T> wo T::T(int, int, int) .

    Wobei es schon irgendwie schräg ist dass

    v.emplace_back({1, 2, 3});
    

    nicht geht,

    auto init = {1, 2, 3};
    v.emplace_back(init);
    

    aber schon.


Anmelden zum Antworten