auto, deleted ctor



  • Hallo out,

    auch mit /Za (Disable Language Extensions) kompiliert es.
    Aber es ist sehr interessant, dass die Erweiterungen default-mässig an sind. Das werde ich in Zukunft überall ausschalten!

    out schrieb:

    Praktisch wird der Copy-Ctor hier wohl nicht verwendet, wegen copy elision.

    Unter http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/?relatedposts_exclude=2119 steht:

    Herb Sutter schrieb:

    By the way, if you’ve been wondering whether that “=” in auto x = expr; causes a temporary object plus a move or copy, wonder no longer: No, it constructs x directly. (See GotW #1.)

    Für mich ist hier nicht ganz klar, ob er meint, dass das normalerweise wegoptimiert wird (also copy elision) oder ob das per Definition immer so sein muss (also, ob hier mehr als copy elision passiert).
    Allerdings kann es eigentlich nicht per Definition immer so sein, da nicht jede Form von auto x = expr; für (N)RVO taugt!?
    Wenn als Konsequenz daraus nur wegoptimiert werden darf (nicht muss), ist es eigentlich logisch, dass der copy-ctor in der obigen Konstellation verfügbar sein muss. Und damit wäre das VS-Compiler Verhalten böse.

    out schrieb:

    Dennoch muss es ein Fehler geben, da der theoretische Ablauf compilierbar sein muss.

    RVO erlaubt zwar, Seiteneffekte von copy-ctors zu ignorieren, aber wohl nicht, dass der copy-ctor nicht zugänglich ist?! Da hast Du wohl Recht.

    Btw...

    auto l = long long{};
    

    compiliert unter VS auch, obwohl das nicht ok ist.


  • Mod

    void* schrieb:

    Unter http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/?relatedposts_exclude=2119 steht:

    Herb Sutter schrieb:

    By the way, if you’ve been wondering whether that “=” in auto x = expr; causes a temporary object plus a move or copy, wonder no longer: No, it constructs x directly. (See GotW #1.)

    Für mich ist hier nicht ganz klar, ob er meint, dass das normalerweise wegoptimiert wird (also copy elision) oder ob das per Definition immer so sein muss (also, ob hier mehr als copy elision passiert).

    Gemeint ist, dass die Initialisierung selbst kein zusätzliches temporäres Objekt erzeugt; nat. kann expr selbst ein temporäres Objekt sein. Der gewählte Konstruktor ist auch nicht zwingend der Copy-/Movekonstruktor - das ist allerdings der typische Fall.

    Betrachte z.B.

    #include <iostream>
    
    struct foo
    {
        foo() { std::cout << "foo()\n"; }
        foo(const foo&) = delete;
        template <typename T>
        foo(T&&) { std::cout << "foo(T&&)\n"; }
    };
    
    foo x{};
    
    foo& lvalue_src() { return x; }
    const foo& const_lvalue_src() { return x; }
    foo prvalue_src() { return x; }
    const foo const_prvalue_src() { return x; }
    foo&& xvalue_src() { return std::move(x); }
    const foo&& const_xvalue_src() { return std::move(x); }
    
    int main()
    {
        std::cout << "a "; auto a = lvalue_src();
    //    std::cout << "b "; auto b = const_lvalue_src();
        std::cout << "c "; auto c = prvalue_src();
        std::cout << "d "; auto d = const_prvalue_src();
        std::cout << "e "; auto e = xvalue_src();
        std::cout << "f "; auto f = const_xvalue_src();
    }
    


  • void* schrieb:

    auch mit /Za (Disable Language Extensions) kompiliert es.

    OK, hät ich jetzt nicht gedacht.

    void* schrieb:

    Unter http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/?relatedposts_exclude=2119 steht:

    Herb Sutter schrieb:

    By the way, if you’ve been wondering whether that “=” in auto x = expr; causes a temporary object plus a move or copy, wonder no longer: No, it constructs x directly. (See GotW #1.)

    Für mich ist hier nicht ganz klar, ob er meint, dass das normalerweise wegoptimiert wird (also copy elision) oder ob das per Definition immer so sein muss (also, ob hier mehr als copy elision passiert).

    Er meint normalerweise:

    Herb Sutter GotW #1 schrieb:

    In practice on most compilers, only a single constructor is called.

    Irgendwo da draußen gibt es bestimmt einen Compiler, der bei der Initialisierung ein Temporary erstellt und dieses dann kopiert.



  • camper schrieb:

    Gemeint ist, dass die Initialisierung selbst kein zusätzliches temporäres Objekt erzeugt;

    Ich verstehe nicht, was Du mit "Initialisierung selbst" meinst. Vielleicht kannst Du mir auf die Sprünge helfen.

    Ich sehe (unoptimiert) maximal folgenden Vorgang:

    auto C = expr;
    

    - Konstruktion eines Objekts A in oder durch expr (ggf. in einer Funktion oder früher falls z.B. global wie x in Deinem Beispiel)
    - Kopie von A in den Stackframe des Aufrufers, dadurch entsteht Objekt B (falls expr ein Funktionsaufruf ist)
    - Kopie von B (oder A) in C

    Welcher dieser Schritte ist für Dich die "Initialisierung selbst" (und welches das temporäre Objekt, das nicht erzeug wird)? Der letzte? Oder meinst Du etwas anderes?

    camper schrieb:

    Der gewählte Konstruktor ist auch nicht zwingend der Copy-/Movekonstruktor - das ist allerdings der typische Fall.

    Vielen Dank für Dein Beispiel mit dem converting template ctor. Diesen Fall hatte ich vollkommen übersehen.

    out schrieb:

    Er meint normalerweise:

    Herb Sutter GotW #1 schrieb:

    In practice on most compilers, only a single constructor is called.

    Irgendwo da draußen gibt es bestimmt einen Compiler, der bei der Initialisierung ein Temporary erstellt und dieses dann kopiert.

    Die Stelle hätte ich selber im GotW #1 selber finden können... 🙄
    Danke!

    Nochmal zurück zur ursprünglichen Frage:
    Aus Euren Erklärungen folgt, dass das was der VS2013 Compiler macht, falsch ist. Richtig? 😃



  • void* schrieb:

    camper schrieb:

    Gemeint ist, dass die Initialisierung selbst kein zusätzliches temporäres Objekt erzeugt;

    Ich verstehe nicht, was Du mit "Initialisierung selbst" meinst. Vielleicht kannst Du mir auf die Sprünge helfen.

    Initialisierung selbst = die Erzeugung des Objekts A. Was davor alles passiert, steht dabei nicht zur Debatte. expr kann ja alles möglich sein, z.B. foo( bar(XYZ()) ) , wobei foo und bar wieder zig Objekte erstellen... Das alles hat mit der Initialisierung selbst nichts zu zun. Nur das, was das Ergebnis von expr ist, hat mit der Initialisierung selbst zu tun.

    Ja, der MSVC verhält sich hier nich standardkonform. Das hat aber weder was mit auto noch mit delete noch mit sonst einem C++11 Feature zu tun. Es ist nicht standardkonform, weil theoretisch ein temporäres Objekt kopiert wird... Ohne Copy-Ctor nicht möglich, also Compilerfehler. Der MSVC hält wohl nicht viel von Theorien 😃 .



  • Also ich dachte bisher, dass

    T t = T(args);
    

    ein Sonderfall ist und immer ohne Kopie funktionieren muss, weil

    T t(args);
    

    in manchen Fällen als Funktionsdeklaration gelten kann.
    Dieses Verhalten hätte ich dann auch auf

    auto t = T(args)
    

    übertragen.

    Falls der Standard hier eine Kopie vorschreibt, finde ich das etwas seltsam, weil er dann eine kopierfreie Konstruktion unmöglich machen würde.

    Aber OK, wieder was gelernt 😉



  • xor schrieb:

    Also ich dachte bisher, dass

    T t = T(args);
    

    ein Sonderfall ist und immer ohne Kopie funktionieren muss

    Ne

    xor schrieb:

    weil

    T t(args);
    

    in manchen Fällen als Funktionsdeklaration gelten kann.

    Na, wenn du da Datentypen angibst bist du selber Schuld. 😃

    xor schrieb:

    Falls der Standard hier eine Kopie vorschreibt

    Genau das tut er ja NICHT. Er sagt ausdrücklich, dass hier copy elision passieren darf. Darum gibts das ja auch im Debug-Modus.


  • Mod

    xor schrieb:

    Also ich dachte bisher, dass

    T t = T(args);
    

    ein Sonderfall ist

    es ist ein Sonderfall insofern, als hier nicht erst eine Konvertierung in den Zieltyp vorgenommen wird, deren Ergebnis dann in das zu initialisierende Objekt kopiert/verschoben wird.

    T t = T(args);
    

    ist 100% äquivalent zu

    T t(T(args));
    

    ausser dass es trotzdem Copyinitialisierung ist, und damit explizite Konstruktoren nicht aufgerufen werden können.



  • out schrieb:

    Na, wenn du da Datentypen angibst bist du selber Schuld. 😃

    Ach da ging es darum, dass

    A a(B());
    

    eine Funktionsdeklaration ist.
    Ich habe aber eben ergoogelt, dass

    A a((B()));
    

    die Konstruktions-Form ist, die ich mit A a = A(B()); eigentlich erreichen wollte.

    OK, damit verstehe ich jetzt auch, warum alles so ist, wie es ist, und dass der Standard doch nichts verbietet 😉

    Also Danke @out und @camper
    ... und wieder was gelernt 😃



  • xor schrieb:

    out schrieb:

    Na, wenn du da Datentypen angibst bist du selber Schuld. 😃

    Ach da ging es darum, dass

    A a(B());
    

    eine Funktionsdeklaration ist.

    Ja ok, das ist wirklich ein doofer Fall, weil du ja Parameternamen weglassen darfst. In dem Fall brauchst dann nochmal eine Klammer drumrum.


Anmelden zum Antworten