Referenz / const Referenz im Funktionsaufruf



  • Hallo,

    ich habe ein Problem bezüglich der Verwendung von Referenzen im Funktionsaufruf und hoffe ihr könnt mir dabei helfen.

    void increment(double& x) 
    {
     x += 1; 
    }
    
    int main()
    {
       double d = 12.34; 
       cout << "d= " << d; 
       increment(d); 
       cout << " -> " << d << endl; 
    
       increment(d+1); 
       increment(1); 
       int n = 7; 
       increment(n); 
    }
    

    Der Aufruf der Funktion increment mit (d+1), (1), (n) führt zu Fehlern bei der Ausführung.

    Wieso ist dies nicht der Fall, wenn ich den Funktionsaufruf mit einer konstanten Referenz durchführe ?

    void increment(const double& x)
    

    Als Begründung wird angegeben, dass beim Aufruf der Funktion der Wert in einen temporären Wert umgewandelt wird, aber dies ist doch auch der Fall wenn ich eine "const Referenz" verwende ?

    Der Wert wird ja trotzdem beim Aufruf in einen temporären double-Wert umgewandelt ?

    mit freundlichen Grüßen,

    Matze



  • An eine const reference kann man auch temporäre Werte binden. Die Lebenszeit verlängert sich dann bis auch die Referenz "zerstört" wird. Ist quasi eine Außname die so im Standard steht. Temporäre Wert an normale Referenzen binden macht auch nicht so viel Sinn. In deinem Beispiel würdest du einen temporären Wert um 1 erhöhen und danach direkt zerstören (wenn der Code compilieren würde). Als extra Falle erlaubt Visual Studio übrigens sowas als nicht standardkonforme Erweiterung. Sehr toll wenn man Code nach Linux portieren will.


  • Mod

    Was meinst du mit Fehlern bei der Ausführung? Die Version ohne const muss bei den Zeilen 13, 14 und 16 Fehler bei der Übersetzung ergeben. Wenn das nicht geschieht, ist dein Compiler kaputt. (Und die Version mit const muss bei Zeile 3 scheitern)

    In allen drei Fällen versuchst du eine non-const Referenz an einen rvalue (also salopp gesagt an einen Wert, dem man nichts zuweisen kann) zu binden, was logischerweise nicht möglich ist, da man sonst schließlich über die Referenz einen unveränderbaren Wert ändern könnte.

    Const Referenzen kann man jedoch mit einem rvalue initialisieren, man kann schließlich keine Änderung am rvalue über die Referenz vornehmen. Handelt es sich bei dem rvalue um einen temporären Wert, so wird dessen Lebenszeit so weit verlängert, wie die Referenz auf diesen Wert lebt. Das ist, was in deinem Programm geschieht.

    PS: Du plenkst.

    edit: zu langsam...



  • proufeD schrieb:

    Als Begründung wird angegeben, dass beim Aufruf der Funktion der Wert in einen temporären Wert umgewandelt wird, aber dies ist doch auch der Fall wenn ich eine "const Referenz" verwende ?

    Der Wert wird ja trotzdem beim Aufruf in einen temporären double-Wert umgewandelt ?

    Ja, nur über die const-Referenz kannst du das temporäre double Objekt nicht ändern (tun wir mal so als gäbe es const_cast & Co nicht).

    Wobei double vielleicht ein schlechtes Beispiel ist. const-Referenzen werden nämlich meist einfach als Optimierung verwendet, damit das übergebene Objekt nicht kopiert werden muss. Im Fall von double bringt das nichts, bzw. kann es sogar die Performance verschlechtern.
    Denk dir statt dessen einfach string oder vector<string> oder eine beliebige Klasse mit potentiell teuer zu kopierenden Objekten.

    void Print(string const& str); // Gibt den Inhalt von "str" aus (wohin auch immer)
    

    Einfache, klare Sache.
    Der Aufruf mit temporären Objekten macht auch ganz klar Sinn, z.B. Print("My name is " + name + ".") . Schön dass das funktioniert.

    Dagegen

    void RemoveTrailingSpaces(string& str); // Entfernt eventuell vorhandene Leerzeichen am Ende des Strings str
    

    Ist jetzt mal grundsätzlich keine total sinnlose Funktion.

    Aber RemoveTrailingSpaces("My name is " + name + ". ") ? Völlig sinnlos.

    Und noch schlimmer: es gäbe dann viele Fälle wo das gewaltig nach hinten losgehen kann, weil Dinge sinnvoll aussehen es aber nicht sind.

    Nimm z.B. an du hast eine Klasse LogMessage , die halt ... verwendet wird um Log-Messages zu speichern/zusammenzubauen 🙂 Und weil der Schöpfer dieser Klasse es so wollte ist diese implizit nach string konvertierbar.
    Du kannst also schreiben

    LogMessage m = MakeMeALogMessage();
    Print(m); // Implizite Konvertierung nach string
    

    Coole Sache, Parker.
    (Ob das gutes Design ist oder nicht ist wieder ne andere Frage, mir fällt auf die schnelle einfach kein besseres Beispiel ein.)

    Wenn wir erlauben würden dass eine string& Referenz an einen temporärern string gebunden wird, dann könnte man allerdings auch schreiben

    LogMessage m = MakeMeALogMessage();
    RemoveTrailingSpaces(m);
    

    Das würde dann kompilieren. Sieht auch total vernünftig aus - RemoveTrailingSpaces(m) entfernt halt die Leerzeichen am Ende von m . Nur dummerweise tut es das nicht. Es entfernt die Leerzeichen am Ende eines temporären string , der danach gleich wieder zerstört wird.



  • Danke an euch drei für eure antworten.
    Jetzt ist es mir klar.

    PS: Das mit dem plenken werde ich versuchen abzustellen 🙂


Anmelden zum Antworten