Rückgabewert zuweisen



  • Hallo!

    Sagen wir es gibt eine externe Klasse (nicht von mir geschrieben) mit folgender Signatur:

    Bar Foo::getBar();
    

    Welche Art des Aufrufs ist dann besser?

    Bar b = foo.getBar(); // 1
    const Bar& b2 = foo.getBar(); // 2
    

    Ich hätte jetzt gesagt Variante 2 ist generell besser, da hier kein CopyCtor aufgerufen wird. Kann man das so sagen?



  • Richtig, bei Variante 2 wird kein Copyctor aufgerufen.
    Die Zeile wird abgeschlossen, der Speicher für den return-Wert freigegeben, deine Referenz zeigt auf Müll und peng.
    Nimm Variante 1, der Compiler ist gut genug zu optimieren (RVO).


  • Mod

    Ich hätte jetzt gesagt Variante 2 ist generell besser, da hier kein CopyCtor aufgerufen wird.

    Was ist besser? In beiden Fällen wird wahrscheinlich kein Kopierkonstruktor aufgerufen, weil in Variante 1 entweder
    - ein Move-Konstruktor aufgerufen wird, wenn verfügbar, sonst ein Kopierkonstruktor
    - Oder die Temporary, die den Rückgabewert hält, elidiert wird, sodass überhaupt nichts gemoved oder kopiert wird. Das bezeichnet man als copy elision.



  • Nathan schrieb:

    Die Zeile wird abgeschlossen, der Speicher für den return-Wert freigegeben, deine Referenz zeigt auf Müll und peng.

    Das ist btw nicht der Fall, die Temporary wird durch const-refs am Leben gehalten. Aber die Argumente für Variante 1 stimmen natürlich.



  • cooky451 schrieb:

    Nathan schrieb:

    Die Zeile wird abgeschlossen, der Speicher für den return-Wert freigegeben, deine Referenz zeigt auf Müll und peng.

    Das ist btw nicht der Fall, die Temporary wird durch const-refs am Leben gehalten. Aber die Argumente für Variante 1 stimmen natürlich.

    Was die Lifetimerverlängerungsregel gilt auch für Funktionsrückgabewerte? Hätt ich nicht gedacht.


  • Mod

    Nathan schrieb:

    Was die Lifetimerverlängerungsregel gilt auch für Funktionsrückgabewerte? Hätt ich nicht gedacht.

    Es ist völlig egal, woher der Initializer kommt. Wichtig ist nur seine Wertkategorie.



  • Arcoth schrieb:

    Nathan schrieb:

    Was die Lifetimerverlängerungsregel gilt auch für Funktionsrückgabewerte? Hätt ich nicht gedacht.

    Es ist völlig egal, woher der Initializer kommt. Wichtig ist nur seine Wertkategorie.

    Nein, ich dachte nur, dass der Rückgabewertspeicher irgendwie was Spezielles ist und das deshalb nicht greift. Wieso gibt es die Regel überhaupt? Nutzt man die irgendwann?



  • Ok, dadurch dass die Referenz const ist, wird also das temporäre Rückgabeobjekt am Leben gehalten? Gilt das auch für non-const Referenzen?

    const
    const Bar& b = foo.getBar();
    Bar& b2 = foo.getBar();
    

    Das Temp Objekt bleibt also am Leben solange b besteht? Wie ist das mit b2?



  • Compile-Error. Eine LValue Referenz bindet nicht an RValues (Temporaries). Mit einer RValue Referenz wuerde das klappen. Fuer const-Lvalue-Ref gibts eine Ausnahmeregelung, darum ist das erlaubt.


  • Mod

    Wieso gibt es die Regel überhaupt? Nutzt man die irgendwann?

    Welche Regel? Dass man Temporaries an Const-Lvalue-Referenzen binden kann? Das ist eine allgemeine Regel.

    Otherwise, the reference shall be an lvalue reference to a non-volatile const type [...], or the reference shall be an rvalue reference.
    - If the initializer expression is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”
    [...]
    then the reference is bound to the value of the initializer expression [...] (or [...] to an appropriate base class subobject).
    - Otherwise, a temporary of type “ cv1 T1” is created and initialized from the initializer expression using the rules for a non-reference copy-initialization (8.5).
    The reference is then bound to the temporary.

    Der Unterschied zwischen Klassen und bspw. Skalaren ist folgender

    A const& a = A(); // Wenn A eine Klasse ist, dann wird a an den Initializer gebunden
    int const& b = 4; // Es wird eine Temporary erstellt, b wird an diese gebunden
    

    Gilt das auch für non-const Referenzen?

    Nein.


  • Mod

    Eine LValue Referenz bindet nicht an RValues (Temporaries).

    Ein rvalue muss nicht auf eine Temporary verweisen.
    Das hier ist genauso ein Fehler:

    int b;
    int& a = static_cast<int&&>(b); // static_cast erzeugt keine Temporary, siehe §5.2.9/3
    

    Fuer const-Lvalue-Ref gibts eine Ausnahmeregelung, darum ist das erlaubt.

    Es ist dieselbe Regelung für rvalue-Referenzen wie für Const-lvalue-Referenzen (Wie macht man das nun mit der Groß- und Kleinschreibung? :D), siehe Zitat.



  • Arcoth schrieb:

    Wieso gibt es die Regel überhaupt? Nutzt man die irgendwann?

    Welche Regel? Dass man Temporaries an Const-Lvalue-Referenzen binden kann? Das ist eine allgemeine Regel.

    Nein, nicht dass man sie binden kann, sondern dass dann die Lifetime verlängert wird. Welchen Sinn macht das?



  • Nathan schrieb:

    Nein, nicht dass man sie binden kann, sondern dass dann die Lifetime verlängert wird. Welchen Sinn macht das?

    Du kannst non-copyables zurückgeben. (C++98)



  • cooky451 schrieb:

    Nathan schrieb:

    Nein, nicht dass man sie binden kann, sondern dass dann die Lifetime verlängert wird. Welchen Sinn macht das?

    Du kannst non-copyables zurückgeben. (C++98)

    Und wie gelangen die in den Returnspeicher?


Log in to reply