1x Copy Konstruktor zu viel


  • Mod

    wie wäre es mit

    json::value v_arr = std::vector<json::value>(1,v_str);
    


  • Hat cooky451 schon vorgeschlagen, bloss halt hinter nem Link versteckt 😉



  • Habt ihr den Standard nicht gelesen?

    §8.5.4/5 schrieb:

    An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list.
    Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array.

    Prinzipiell lässt sich das Problem auf http://ideone.com/bMkth9 reduzieren. Wie man sieht, wird zweimal Kopiert. Wieso? Einmal wird a für die initializer_list kopiert, und dann wird die Kopie noch einmal Kopiert, denn der initializer_list -Konstruktor von vector ist ja definiert als...

    Table 100 — Sequence container requirements schrieb:

    X(il); Equivalent to X(il.begin(), il.end())



  • Habt ihr den Standard nicht gelesen?

    Nein. 🙂

    Danke fuer eure Muehen.



  • Sone schrieb:

    Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array.

    Nur, um sicher zu gehen, dass das keiner falsch versteht: Kopier-Initialisierung impliziert keine Kopie. Das kann auch ein Move sein oder ganz wegoptimiert werden. Kopierinitialisierung ist auch das, was bei der Initialisierung von Funktionsparametern oder dem hier stattfindet:

    unique_ptr<int> foo();
    
    int main() {
      unique_ptr<int> bar = foo();  // Kopierinitialisierung
      double x = 23;                // Kopierinitialisierung
      double& r = x;                // Kopierinitialisierung
    }
    

    Das heißt einfach so, wobei im ersten Fall hier höchstens "gemoved" wird (wenn die Move-Konstruktion nicht gerade wegoptimiert wird) und im zweiten Fall im wesentlichen nur implizit ein int in ein double umgewandelt wird und im dritten Fall nur die Referenz initialisiert wird. Dem gegenüber steht die Direktinitialisierung:

    unique_ptr<int> foo();
    
    int main() {
      unique_ptr<int> bar (foo());  // Direktinitialisierung
      double x (23);                // Direktinitialisierung
      double& r (x);                // Direktinitialisierung
    }
    

    Der Unterschied dürfte auch bekannt sein: Bei der Kopierinitialisierung können nur Konstruktoren verwendet werden, die sich auch mit einem Parameter aufrufen lassen und nicht als explicit mariert wurden. Ob da jetzt was kopiert wird oder Peng, ist völlig egal.



  • Sone schrieb:

    Habt ihr den Standard nicht gelesen?

    Nein, hab' ich nicht. Geht das aus meinem Beitrag nicht hervor dass ich das nicht habe 🤡
    Aber ich hab' wenigstens richtig vermutet, das reicht mir. 🙂



  • @krümelkacker
    Also erstmal meine ich dass der Standard genauestens alle Stellen beschreibt wo copy-elision erlaubt ist. Und alles was nicht erlaubt ist ist verboten.

    Und zweitens... da steht dass es sich um ein Array handelt, und ich behaupte mal dass ein Array aus T und nicht ein Array aus T* gemeint ist.

    Und wenn das Ding das kopiert wird ne LValue ist, dann bleibt halt nix anderes übrig als eine echte Kopie zu machen.
    Dass er ne RValue reinmoven könnte ist klar, aber in dem Beispiel ist's halt keine RValue.



  • Habe ich das nun richtig verstanden: Das Element wird in die Init-Liste reinkopiert und dann wird die Init-Liste in den Vector kopiert?


  • Mod

    knivil schrieb:

    Habe ich das nun richtig verstanden: Das Element wird in die Init-Liste reinkopiert und dann wird die Init-Liste in den Vector kopiert?

    Korrekt, d.h. das Element, das in der Init-Liste steckt, wird kopiert.



  • Kopier-Initialisierung impliziert keine Kopie.

    Hat das jemand gesagt?

    If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered.
    The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3).

    Es kann und wird dann also per nicht-explizitem Move-Konstruktor gemoved werden, wenn

    • Ein "applikabler" Move-Konstruktor definiert ist (also kein explizit oder implizit als deleted definierter).
    • Der Initializer-Ausdruck ein rvalue* oder ein function lvalue ist.

    *Also ein xvalue oder prvalue



  • Sone schrieb:

    Kopier-Initialisierung impliziert keine Kopie.

    Hat das jemand gesagt?

    Ich habe auch nicht behauptet oder implizieren wollen, dass du oder jemand das gesagt hätte. Das hätte man vielleicht an meiner Einleutung erkennen können, die du nicht zitiert hast.

    hustbaer schrieb:

    ...
    Und wenn das Ding das kopiert wird ne LValue ist, dann bleibt halt nix anderes übrig als eine echte Kopie zu machen.
    Dass er ne RValue reinmoven könnte ist klar, aber in dem Beispiel ist's halt keine RValue.

    Naja, std::cref(sonstwas) ist schon ein Rvalue-Ausdruck. Aber stimmt, eine Move-Konstruktion eines value-Objekts bei so einem Initialisierer kann und sollte man natürlich nicht erwarten. Und ich hoffe mal, dass das auch nicht so rübergekommen ist, als würde ich etwas anderes behaupten.


Anmelden zum Antworten