1x Copy Konstruktor zu viel



  • http://en.cppreference.com/w/cpp/utility/initializer_list

    std::initializer_list::iterator ist const T* , also Move-blockend.
    Kann nicht gehen würd ich sagen.



  • hustbaer schrieb:

    http://en.cppreference.com/w/cpp/utility/initializer_list

    std::initializer_list::iterator ist const T* , also Move-blockend.
    Kann nicht gehen würd ich sagen.

    Ich moechte das Element von vector nicht moven, es sollte einmal in den vector kopiert werden. Er wird aber insgesamt 2x kopiert. Ausgabe:

    move constructor string
    copy constructor value
    copy constructor value
    move constructor array
    

    Knackpunkt ist wahrscheinlich:

    The storage for std::initializer_list is unspecified (i.e. it could be automatic, temporary, or static read-only memory, depending on the situation).

    Kommt das so hin?

    Ja, vielleicht 🙂

    Ich mag Initialisierungslisten nicht mehr ...



  • @knivil
    Hmmmmmmm, ja.
    Pfuh.
    *grübel*

    Da müsste ich jetzt im Standard nachlesen. Hab mich nicht wirklich genau mit std::initializer_list beschäftigt.
    Ich habe halt das...

    cppreference.com schrieb:

    Initializer lists may be implemented as a pair of pointers or pointer and length. Copying a std::initializer_list does not copy the underlying objects. The underlying array is not guaranteed to exist after the lifetime of the original initializer list object has ended

    ...so verstanden dass das "original initializer list object" schon eine Kopie der Objekte enthalten muss, nicht bloss z.B. ein Array aus Zeigern auf die Elemente die innerhalb der {} stehen.

    Wenn diese Interpretation richtig ist, dann ist klar dass 2x kopiert werden muss. Eine Kopie rein in die initializer list, und wegen des const T* Iterators nochmal eine Kopie von der initializer list in den Vektor.

    Und dein Versuch mit cref funktioniert nicht, weil der initializer list ctor von std::vector<T> eben nur eine std::initializer_list<T> annimmt, und keine std::initializer_list<T const&> , was ja krümelkacker schon geschrieben hat.


  • 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