std::map::try_emplace



  • Die Funktion istja neu mit C++17 dazu gekommen und hat den Vorteil, dass im falle, dass der Key schon in der Map existiert, der mapped Type gar nicht erst konstruiert wird (im gegenteil zu std:­čŚ║:emplace.

    Nun habe ich folgende Klasse:

    struct Foo{
       Foo(){ std::cout << "Default ctor\n";}
       ~Foo(){ std::cout << "Dtor \n";}
    };
    

    und m├Âchte jetzt ├╝ber die Map laufen und nur wenn der Key noch nicht vorhanden ist, eine Instanz von std::vector<Foo> einf├╝gen.

    std::map<size_t,std::vector<Foo>> map_;
    
    for (size_t i = 0; i < 5, ++i) 
      map_.try_emplace(i, Foo(());
    
    for (size_t i = 0; i < 5, ++i) 
      map_.try_emplace(i, Foo(());
    

    jedoch ruft er auch beim zweiten Schleifendurchlauf den Ctor von Foo auf nur um danach unmittelbar den Destructor aufzurufen.

    Was mache ich hier falsch?

    danke f├╝r eure Hilfe



  • Sewing schrieb:

    und m├Âchte jetzt ├╝ber die Map laufen und nur wenn der Key noch nicht vorhanden ist, eine Instanz von std::vector<Foo> einf├╝gen.

    Und warum f├╝gst du dann eine Instanz von Foo ein?



  • Ich f├╝ge implizit einen ein-elementigen vector ein, die Signatur ist

    pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
    

    hier das gleiche

    map_.try_emplace(i, std::vector<Foo>{ {} });
    


  • Die Frage war nicht "wie geht das" sonder "warum"?

    map_.try_emplace(i, {} };
    

    ?



  • Nein, ich hab mich wohl etwas unklar ausgedr├╝ckt ; )

    Wenn der key schon enthalten ist, wird ja kein Vector konstruiert, wohl aber ein default-initialized Foo Object nur um es dann direkt wieder zu verwerfen.

    Und ich w├╝sste gerne, wie ich das so hinbekomme, dass nicht einmal Foo angelegt wird, im Falle eines bereits vorhandenen Keys



  • Da die Parameter immer zuerst ausgewertet werden, bevor ein Funktionsaufruf stattfindet, mu├čt du explizit dann selber die Abfrage durchf├╝hren:

    if (map_.count(i) == 0) // oder !count(i)
      map_.emplace(i, Foo());
    

    Als Abhilfe k├Ânntest du selber eine Funktion mit einem Funktionsparameter erzeugen und diese mit einem Lambda-Ausdruck aufrufen:

    map_try_emplace(map, [] { return Foo(); });
    


  • W├Ąre nicht die einfachere L├Âsung, einfach {} zu try_emplacen und den R├╝ckgabewert von try_emplace zu nutzen?

    auto [it, inserted] = map_.try_emplace(i, {});
    if (inserted) {
      it->reserve(...);
      it->emplace_back(...);
    }
    

    oder so ├Ąhnlich?

    Ich denke, man sollte den doppelten map-Lookup aus der 1. Alternative von The69 vermeiden.



  • aber eure Vorschl├Ąge gehen ja vollkommen vorbei an dem, wof├╝r try_emplace gedacht ist

    habe in einem Buch folgendes gefunden

    http://666kb.com/i/dqug8l5meu77z7za5.jpg



  • ich glaube das problem ist, dass ich versuche den vector mit rvalues zu initialisieren. Das Problem l├Ąsst sich wohl l├Âsen indem man den vector in eine struct wrapped und die struct wird ja dann nur constructed, wenn der Key nicht schon vorhanden ist



  • Ich glaube, wir reden irgendwie aneinander vorbei. Erzeugt wird doch immer etwas, weil du ein Argument erzeugst.

    try_emplace hei├čt doch einfach nur, wenn ein Key existiert, bleibt der Container unver├Ąndert. Das Argument spielt dann dabei gar keine Rolle (f├╝r den Container).

    PS: Welches Buch ist das?



  • Eben nicht!

    der besondere Reiz von try_emplace liegt in der Tatsache begr├╝ndet, dass

    "will not construct the object associated with the key, if the key already exists..."



  • Sewing schrieb:

    Eben nicht!

    der besondere Reiz von try_emplace liegt in der Tatsache begr├╝ndet, dass

    "will not construct the object associated with the key, if the key already exists..."

    Richtig, das bezieht sich aber nicht auf den Funktionsaufruf, sondern auf das was intern in der Funktion try_emplace passiert.

    Wie willst du bei einem Funktionsaufruf verhindern, dass ein Arguement erzeugt wird, wenn du eins ├╝bergeben willst und die Funktion eines erwartet: fkt( Foo() )?

    Ich sehe gerade, dass Th69 genau das auch schon geschrieben hat ­čĄí



  • Doppelt h├Ąlt besser - vllt. versteht Sewing es ja jetzt!?



  • habs jetzt hoffe ich verstanden:

    std::map<int,std::string> m_ {1, "myString"};
    
    m_.try_emplace(1, "Test");
    

    erschafft zwar nen rvalue "Test" aber nie ein std::string mitels entsprechendem ctor aufruf. In meinem anf├Ąnglichen Beispiel w├╝rde also nie ein std::vector<Foo> erschaffen, wohl aber ein temporary Foo.

    Hintergrund ist, dass mein vector so eine Art std::anys enh├Ąlt, die ich dann jeweils mit verschiedenne Objekten iniitalisieren will. Diese Objekte werden dann aber eben jedes Mal schon erzeugt, nur der std::vector eben nicht

    Habe mir jetzt so beholfen

    struct Collection{
       Collection() : anys_{{Foo{}, Foo{}}}
    
    std::vector<Any> anys_;
    };
    
    std::map<int,Collection> m_;
    
    m_.try_emplace(1, {});
    

    auf diese Weise m├╝sste es funktionieren oder?



  • Nein, das geht so nicht.

    Mit deinem Code:

    m_.try_emplace(1, Collection{});
    

    Noch BEVOR try_emplace aufgerufen wird, muss wird ja die beiden Argumente, also 1 und auch dein Collection-Objekt erzeugt (das dann ja im Constructor 2x Foo erzeugt).

    Da try_emplace die Argumente ja an den Constructor weiterleitet, solltest du hier einfach KEIN 2 Argument angeben, da dein Constructor ja auch kein Argument hat.

    Schau dir das mal an:

    #include <vector>
    #include <map>
    #include <iostream>
    
    struct Foo {
       Foo() { std::cout << "Foo create\n"; }
    };
    
    struct Collection{
       Collection() : anys_{{Foo{}, Foo{}}} {}
       std::vector<Foo> anys_;
    };
    
    int main() {
       std::map<int,Collection> m;
       std::cout << "Erfolgreiches emplace\n";
       m.try_emplace(1);
       std::cout << "Emplace, wenn schon existiert\n";
       m.try_emplace(1);
    }
    


  • also grunds├Ątzlich ist bei meinem vorgehen da composition in einer struct die richtige wahl oder?

    hatte meinen code oben schon editiert zu

    m_.try_emplace(1, {});
    

    leeres Klammerpaar, das m├╝sste ja auch funktionieren nehnme ich an

    und wenn ich als argument nen string ├╝bergeben m├Âchte

    struct Collection{
       Collection(std::string& s) : anys_{{Foo{s}, Foo{s}}}
    
    std::vector<Any> anys_;
    };
    
    std::map<int,Collection> m_;
    
    m_.try_emplace(1, "MyString");
    

    sollte ebenfalls funktionieren oder? das erzeugt ja nur nen temporary string, aber das Collection Objekt wird mithilfe dieses strings nur erzeugt, wenn der key nicht schon vorhanden ist

    danke f├╝r eure Hilfe ; )


Log in to reply