Allgemeine Frage: Wird der Rückgabewert beim Verlassen der Funktion kopiert? (C und C++)


  • Gesperrt

    Ich frage mich gerade, ob der Rückgabewert beim Verlassen kopiert oder nur referenziert wird, und weshalb ein std::vector returned werden kann? Und der "Funktionsrumpf" wäre dann doch eigentlich nicht mehr call-by-value, oder? Danke, wer Licht ins Dunkle bringt.


  • Mod

    Rückgabewerte werden kopiert. Oder zumindest verhalten sie sich so, als würden sie kopiert. Der Compiler ist aber angehalten, möglichst zu schummeln, wo er nur kann. Das heißt, anstatt den Rückgabewert erst im Stack der Funktion (was auch immer das überhaupt genau heißen mag) zu bauen, und dann in den Stack des Aufrufers zu kopieren, wird er sofort an der richtigen Stelle gebaut, so dass eine Kopie gar nicht notwendig ist. Es ist sogar ausdrücklich erlaubt (sogar gefordert!), dass dadurch das beobachtbare Verhalten von einer echten Kopie abweichen darf, wenn dabei Kopier- oder Movekonstruktoren eingespart werden.

    https://en.cppreference.com/w/cpp/language/copy_elision

    Große, schwergewichtige Objekte gibt man aber in der Regel trotzdem per Parameterreferenz zurück. Das drückt besser die Absicht des Programmierers aus und man muss sich nicht auf irgendwelche Optimierungen verlassen.

    Allgemeine Sequenzen gibt man in der Regel nochmal ganz anders zurück, nämlich indem man sie in einem vom Nutzer gegebenen Outputiterator schreibt, wodurch man ohne jeden Nachteil jede Menge Flexibilität für den Nutzer gewinnt.


  • Gesperrt

    Vielen Dank. Sie scheinen sich gut damit auszukennen. Woher wissen Sie das alles?

    Eine Frage noch, also wäre bei Referenzen auf den Kopierkonstruktor kein Verlass?



  • @TaucherOne sagte in Allgemeine Frage: Wird der Rückgabewert beim Verlassen der Funktion kopiert? (C und C++):
    ...

    Eine Frage noch, also wäre bei Referenzen auf den Kopierkonstruktor kein Verlass?

    Auf den Kopierkonstruktor braucht man sich da nicht verlassen zu können, er würde bei Benutzung von Referenzen überhaupt nicht aufgerufen. Das Beispiel, das @SeppJ angesprochen hat, sähe dann so aus:

    void initialize( SomeComplexObject& obj )
    {
       // stelle Initialzustand von obj her und wirf ggf. eine exception, falls iwas schiefgegangen ist
    }
    
    int main()
    {
       SomeComplexObject myObject;
       initialize( myObject );
    }
    

    Das Verhalten bei Rückgabewerte kann man sich in Godbolt's Compiler Explorer sehr schön ansehen, in einem Panel schreibt man C++ Quelltext und in einem anderen kann man sehen, welchen Objektcode der Compiler daraus erzeugt.


Anmelden zum Antworten