Zeiger auf Rückgabewert


  • Mod

    groovemaster schrieb:

    Wlu schrieb:

    Ich dachte immer bei einem Rückgabewert wird ein temporäres Object erstellt

    Sofern die Rückgabe by-value geschieht, was hier ja der Fall ist, hast du Recht.

    Wlu schrieb:

    und dieses bleibt so lange erhalten wie der Ausdruck.

    Es bleibt solange erhalten, bis der Ausdruck, in dem es erzeugt wurde, abgearbeitet wurde. Hier wäre das

    foo()
    

    ISO/IEC 14882:2003 schrieb:

    12.2/3
    When an implementation introduces a temporary object of a class that has a non-trivial constructor (12.1), it shall ensure that a constructor is called for the temporary object. Similarly, the destructor shall be called for a temporary with a non-trivial destructor (12.4). Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception.

    Das Temporary, das als Ergebnis von foo() entsteht, stirbt erst, nachdem foo2 abgearbeitet wurde. Der besagte Fehler muss also eine andere Ursache haben. Aus dem Text geht nicht hervor, ob Foo einen eigenen Operator & hat. Wenn nicht, so ist das Beispiel bereits ill-formed, und der Fehler möglicherweise in einer fehlerhaften Implementation der Spracherweiterung zu suchen.



  • Nur mal als Zwischenfrage: ist es nicht etwas... verwirrend den Übergabeparameter der Funktion und auch die static Variable a zu nennen?



  • camper schrieb:

    Das Temporary, das als Ergebnis von foo() entsteht, stirbt erst, nachdem foo2 abgearbeitet wurde.

    Und wo steht das in deinem zitierten Text? Da steht, dass des temporäre Objekt stirbt im letzten Schritt bei der Auswertung der "full-expression", in welcher es erzeugt wurde. Die Frage ist doch, wo wird es erzeugt und wo ist hier die full-expression? Erzeugt wird es durch den Aufruf von foo(). Und genau dieser Aufruf ist eine full-expression. Zu finden ist dies unter 1.9-13, was in deinem zitierten Text auch referenziert ist. Und Ausnahmen, so dass das temporäre Objekt länger lebt, habe ich ja bereits angesprochen. ZB für die Initialisierung eines anderen Objektes oder das Binden an eine Referenz auf const. Es gibt aber keine Garantie, dass das temporäre Objekt beim Aufruf von foo2() noch lebt.

    Shinja schrieb:

    Nur mal als Zwischenfrage: ist es nicht etwas... verwirrend den Übergabeparameter der Funktion und auch die static Variable a zu nennen?

    Praktisch schon. Für das Beispiel aber vernachlässigbar.


  • Mod

    groovemaster schrieb:

    camper schrieb:

    Das Temporary, das als Ergebnis von foo() entsteht, stirbt erst, nachdem foo2 abgearbeitet wurde.

    Und wo steht das in deinem zitierten Text? Da steht, dass des temporäre Objekt stirbt im letzten Schritt bei der Auswertung der "full-expression", in welcher es erzeugt wurde. Die Frage ist doch, wo wird es erzeugt und wo ist hier die full-expression? Erzeugt wird es durch den Aufruf von foo(). Und genau dieser Aufruf ist eine full-expression. Zu finden ist dies unter 1.9-13, was in deinem zitierten Text auch referenziert ist. Und Ausnahmen, so dass das temporäre Objekt länger lebt, habe ich ja bereits angesprochen. ZB für die Initialisierung eines anderen Objektes oder das Binden an eine Referenz auf const. Es gibt aber keine Garantie, dass das temporäre Objekt beim Aufruf von foo2() noch lebt.

    1.9/13 betrifft Konstrukte, die keine Ausdrücke sind; das interessiert uns hier nicht. 1.9/12 dagegen ist in dieser Hinsicht sehr klar. Ein "vollständiger Ausdruck" ist einer, der nicht Teil eines anderen Ausdrucks ist.
    in

    foo2(&foo2());
    

    ist foo() aber (ganz offensichtlich) Teil des gesamten Ausdrucks, der dieses Statement bildet. Beim Vergleich von 12.2/3 Satz 3 mit 12.2/5 Satz 4 erkennt man ebenso, dass das Binden an einen Referenzparameter eben nichts an der Lebenszeit des Temporaries ändert. Das ist gewissermaßen die Ausnahme von der Ausnahme.

    Es kann keinen Zweifel geben, dass der Wortlaut - wie man ihn auch interpretieren mag - ein bestimmtes Verhalten fordert. Es ist jedenfalls nicht dem Compiler überlassen, wie lange Temporaries leben. Von einer fehlenden Garantie zu reden, ist hier verfehlt. Der ultimative Praxistest, der nat. völlig bedeutungslost ist, scheint mir recht zu geben (vc8 w/ /Za):

    #include <iostream>
    #include <ostream>
    using namespace std;
    
    struct Foo
    {
        Foo() { cout << "ctor " << (void*)this << endl; }
        Foo(const Foo&) { cout << "copy-ctor " << (void*)this << endl; }
        ~Foo() { cout << "dtor " << (void*)this << endl; }
        Foo* operator&() { return this; }
    };
    
    Foo f()
    {
        Foo foo;
        return foo;
    };
    
    void g(Foo* p)
    {
        cout << (void*)p << endl;
    }
    
    int main()
    {
        g(&f());
    }
    

    Aber jeder soll den Compiler seines Vertrauens benutzen.



  • Könnte es sein, dass der Vc++ 2005 Express genrell manchmal etwas
    'komischer' ist? Er akzeptiert Code den ich beim g++ niemals cpmpiliert bekommen hätte.

    Eine Frage noch. Wo lest ihr immer alles nach wie sich der Compiler verhällt?



  • camper schrieb:

    1.9/13 betrifft Konstrukte, die keine Ausdrücke sind

    Und wo genau soll das stehen?

    camper schrieb:

    Ein "vollständiger Ausdruck" ist einer, der nicht Teil eines anderen Ausdrucks ist.

    Ist ja schön und gut. Was "full-expression" bzw. subexpression bedeutet, wird dadurch trotzdem nicht geklärt.

    camper schrieb:

    in

    foo2(&foo2());
    

    ist foo() aber (ganz offensichtlich) Teil des gesamten Ausdrucks

    Damit machst du ja mehr als deutlich klar, dass es eine reine Vermutung deinerseits ist. Fakten bitte!

    camper schrieb:

    Beim Vergleich von 12.2/3 Satz 3 mit 12.2/5 Satz 4 erkennt man

    genau das, was ich bereits geschrieben habe. ➡

    Da steht, dass des temporäre Objekt stirbt im letzten Schritt bei der Auswertung der "full-expression", in welcher es erzeugt wurde.

    Und Ausnahmen, so dass das temporäre Objekt länger lebt, habe ich ja bereits angesprochen. ZB für [...] das Binden an eine Referenz

    Wo du hier erkennst, dass "das Binden an einen Referenzparameter eben nichts an der Lebenszeit des Temporaries ändert", ist mir unklar. Genauso was hier "die Ausnahme von der Ausnahme" sein soll.

    camper schrieb:

    Es kann keinen Zweifel geben, dass der Wortlaut - wie man ihn auch interpretieren mag - ein bestimmtes Verhalten fordert. Es ist jedenfalls nicht dem Compiler überlassen, wie lange Temporaries leben.

    Nein, davon habe ich auch gar nicht gesprochen. Der Compiler muss eine Mindestlebenszeit garantieren bzw. dafür sorgen, dass ein Objekt spätestens zu einem gewissen Zeitpunkt zerstört wird. Alles darüber hinaus ist Teil der Implementation. Und so wie ich es verstehe, lebt der return Wert von foo() nicht mehr, wenn die Ausführung von foo2() beginnt. Sollte ein Compiler das Objekt erst später löschen, so ist das implementationsspezifisch.

    camper schrieb:

    Der ultimative Praxistest [...]

    Was soll das denn für ein ultimativer Praxistest sein? Das ist doch eine reine Beobachtung der Implementation. GCC erstellt hier zB überhaupt kein temporäres Objekt und verwendet direkt foo aus f(). Letztendlich spielt das hier auch keine allzu grosse Rolle, da der Code von Wlu sowieso nicht standardkonform ist. Der address-of Operator darf nunmal nur auf lvalues angewendet werden, der Rückgabewert von foo() ist jedoch ein rvalue.

    Wlu schrieb:

    Eine Frage noch. Wo lest ihr immer alles nach wie sich der Compiler verhällt?

    Im Standard. Aktuell ist ISO/IEC 14882:2003(E).



  • Warum kostet der Standart als Buch denn 370?Selbstnurdiepdfbringtesnochauf280? Selbst nur die pdf bringt es noch auf 280.
    Ist der Standart nicht irgendwo anders frei zugänlich?


  • Mod

    groovemaster schrieb:

    camper schrieb:

    1.9/13 betrifft Konstrukte, die keine Ausdrücke sind

    Und wo genau soll das stehen?

    1.9/13
    [Note: certain contexts in C + + cause the evaluation of a full-expression that results from a syntactic construct other than expression (5.18). For example, in 8.5 one syntax for initializer is ( expression-list ) but the resulting construct is a function call upon a constructor function with expression-list as an argument list; such a function call is a full-expression. For example, in 8.5, another syntax for initializer is = initializer-clause but again the resulting construct might be a function call upon a constructor function with one assignmentexpression as an argument; again, the function call is a full-expression. ]

    Es geht hier um durch Komma getrennte Ausdrücke, ohne dass dieses Komma der Kommaoperator im Sinne von 5.18 ist.

    camper schrieb:

    Ein "vollständiger Ausdruck" ist einer, der nicht Teil eines anderen Ausdrucks ist.

    groovemaster schrieb:

    Ist ja schön und gut. Was "full-expression" bzw. subexpression bedeutet, wird dadurch trotzdem nicht geklärt.

    Es ist wahr, dass der Begriff "subexpression" nicht explizit definiert wird. Dieses Schicksal teil er mit anderen Begriffen, das macht ihn aber nicht unbestimmt. Tatsächlich kann und sollte man diesen Satz 1.9/12

    A full-expression is an expression that is not a subexpression of another expression. If a language construct is defined to produce an implicit call of a function, a use of the language construct is considered to be an expression for the purposes of this definition.

    als Definition beider Begriffe auffassen. Der Begriff "subexpression" bezeichnet einen Ausdruck (im Sinne von Abschnitt 5), der durch Weglassen mindestens eines syntaktischen Elements aus einem anderen Ausdrucks entsteht (andernfalls wird der Begriff von "full-expression" unsinnig). Es fällt mir schwer zu verstehen, wie hier Auslegungszweifel bestehen können.

    camper schrieb:

    in

    foo2(&foo2());
    

    ist foo() aber (ganz offensichtlich) Teil des gesamten Ausdrucks

    Damit machst du ja mehr als deutlich klar, dass es eine reine Vermutung deinerseits ist. Fakten bitte!

    Das ist nur die Anwendung der obigen Definition auf unser Besispiel.
    Auf den Rest muss ich nicht eingehen, weil das nur logische Folge unseres unterschiedlichen Verständnissens in diesm Punkt ist.

    Was soll das denn für ein ultimativer Praxistest sein? Das ist doch eine reine Beobachtung der Implementation.

    Natürlich, weshalb das ganze auch keinen Beweischarakter haben kann. Andererseits lässt deren Verhalten auf die Interpretation der Problematik durch den Compilerhersteller schließen und somit indirekt darauf wie der Standard zu verstehen ist oder verstanden werden kann. Der Test soll nur zeigen, ob der Zeiger, den g() bekommt, während der Ausführung von g() auf ein lebendes Objekt verweist, und du behauptest ja, dass das nicht unbedingt der Fall sein muss.



  • Wlu schrieb:

    Warum kostet der stan**** als Buch denn 370?Selbstnurdiepdfbringtesnochauf280? Selbst nur die pdf bringt es noch auf 280.
    Ist der stan**** nicht irgendwo anders frei zugänlich?

    http://www.kuzbass.ru:8086/docs/isocpp/



  • camper schrieb:

    [Note: certain contexts in C + + cause the evaluation of a full-expression that results from a syntactic construct other than expression (5.18). For example, in 8.5 one syntax for initializer is ( expression-list ) but the resulting construct is a function call upon a constructor function with expression-list as an argument list; such a function call is a full-expression. For example, in 8.5, another syntax for initializer is = initializer-clause but again the resulting construct might be a function call upon a constructor function with one assignmentexpression as an argument; again, the function call is a full-expression. ]

    Es geht hier um durch Komma getrennte Ausdrücke, ohne dass dieses Komma der Kommaoperator im Sinne von 5.18 ist.

    Ich dachte, es geht um keine Ausdrücke? So war jedenfalls dein Wortlaut. Wie auch immer... Genau um diese Komma getrennten Ausdrücke geht es eben nicht, bzw. den anderen Ausdrücken, die die Auswertung eines Ausdrucks als full-expression veranlassen.

    camper schrieb:

    Es ist wahr, dass der Begriff "subexpression" nicht explizit definiert wird. Dieses Schicksal teil er mit anderen Begriffen, das macht ihn aber nicht unbestimmt. Tatsächlich kann und sollte man diesen Satz 1.9/12

    A full-expression is an expression that is not a subexpression of another expression. If a language construct is defined to produce an implicit call of a function, a use of the language construct is considered to be an expression for the purposes of this definition.

    als Definition beider Begriffe auffassen. Der Begriff "subexpression" bezeichnet einen Ausdruck (im Sinne von Abschnitt 5), der durch Weglassen mindestens eines syntaktischen Elements aus einem anderen Ausdrucks entsteht (andernfalls wird der Begriff von "full-expression" unsinnig). Es fällt mir schwer zu verstehen, wie hier Auslegungszweifel bestehen können.

    Was machst du zB damit

    Note: the evaluation of a full-expression can include the evaluation of subexpressions that are not lexically part of the full-expression.

    Was für mich im Endeffekt bedeutet, dass es innerhalb der Auswertung einer full-expression Teile geben kann, die jedoch nicht zur full-expression gehören, also wiederum selbst full-expressions sein könnten. Versteh mich nicht falsch, ich empfinde es durchaus als logisch, dass du betreffenden Code als full-expression ansiehst. Nur fehlt mir seitens des Standards die eindeutige Definition dafür. Die Suche im Netz hat mir diesbzgl. auch nicht sonderlich weitergeholfen. Offensichtlich gibt es da noch einige Verständnisprobleme. Die meisten Compiler implementieren scheinbar die von dir angesprochene Vorgehensweise. Der Grund dafür ist zum einen wohl im Stack Prinzip von automatischen Objekten zu suchen, also was zuerst erzeugt wird, wird auch zuletzt zerstört. Zum anderen hat das auch praxisbezogene Gründe. Folgendes würde bei einem Compiler nicht funktionieren, wenn er das Temporary zu früh löscht:

    void foo(const char* s);
    //...
    foo(std::string("hallo").c_str())
    

    AFAIK sollen ältere GCC Versionen aber genau sowas gemacht haben. Darüber hinaus gibt es wohl auch Compiler, die Temporaries erst am Ende des Gültigkeitsbereiches löschen.

    camper schrieb:

    Andererseits lässt deren Verhalten auf die Interpretation der Problematik durch den Compilerhersteller schließen und somit indirekt darauf wie der Standard zu verstehen ist

    Eben das sehe ich nicht so. Nur weil zB eine Implementation int mit 32 Bit definiert, heisst das noch lange nicht, dass dies durch den Standard vorgegeben wird. Und die inkonsistente Behandlung von Temporaries sagt mir eher, dass es hier noch kein einheitliches Konzept gibt, was wiederum auf Interpretationsfreiheiten im Standard schliessen lässt. Deshalb sehe ich deinen "ultimativen Praxistest" nicht wirklich als Beweis. Es ist eher eine Spielerei, um zu sehen, wie der jeweilige Compiler reagiert.

    Letztendlich könnten wir noch ohne Ende weiterdiskutieren, auf einen grünen Zweig kommen wir wohl 'eh nicht. Und wie ich bereits sagte, der Code von Wlu ist sowieso nicht standardkonform. Daher sehe ich auch keinen Sinn darin, den Standard weiter nach einem definierten Verhalten zu durchforsten, was er sowieso für ungültig erklärt. Die Frage bestand für mich deshalb lediglich darin, warum die Implementation mit einem Speicherzugriffsfehler reagiert. Ein plausibler Grund wäre eben der, dass das temporäre Objekt zu früh gelöscht wird. Letztendlich können die Ursachen aber auch an ganz anderer Stelle liegen. Ohne den kompletten Code von Wlu ist das aber nicht zweifelsfrei zu beantworten. So long...


  • Mod

    groovemaster schrieb:

    camper schrieb:

    [Note: certain contexts in C + + cause the evaluation of a full-expression that results from a syntactic construct other than expression (5.18). For example, in 8.5 one syntax for initializer is ( expression-list ) but the resulting construct is a function call upon a constructor function with expression-list as an argument list; such a function call is a full-expression. For example, in 8.5, another syntax for initializer is = initializer-clause but again the resulting construct might be a function call upon a constructor function with one assignmentexpression as an argument; again, the function call is a full-expression. ]

    Es geht hier um durch Komma getrennte Ausdrücke, ohne dass dieses Komma der Kommaoperator im Sinne von 5.18 ist.

    Ich dachte, es geht um keine Ausdrücke?

    Richtig, Das gesamte Konstrukt (einzelne Ausdrücke plus Kommas) ist kein Ausdruck, sondern eine Parameter- oder Initialisierungsliste.

    groovemaster schrieb:

    camper schrieb:

    Es ist wahr, dass der Begriff "subexpression" nicht explizit definiert wird. Dieses Schicksal teil er mit anderen Begriffen, das macht ihn aber nicht unbestimmt. Tatsächlich kann und sollte man diesen Satz 1.9/12

    A full-expression is an expression that is not a subexpression of another expression. If a language construct is defined to produce an implicit call of a function, a use of the language construct is considered to be an expression for the purposes of this definition.

    als Definition beider Begriffe auffassen. Der Begriff "subexpression" bezeichnet einen Ausdruck (im Sinne von Abschnitt 5), der durch Weglassen mindestens eines syntaktischen Elements aus einem anderen Ausdrucks entsteht (andernfalls wird der Begriff von "full-expression" unsinnig). Es fällt mir schwer zu verstehen, wie hier Auslegungszweifel bestehen können.

    Was machst du zB damit

    Note: the evaluation of a full-expression can include the evaluation of subexpressions that are not lexically part of the full-expression.

    Meine Definition ist offenbar noch nicht ganz präzise, das ist mir gestern nach dem Schreiben schon aufgefallen. Es ist wohl kein Zufall, dass der Standard hier auch nicht deutlicher wird. Wenn du mir den Paragrafen nennst, schau ich nochmal nach, aber ich nehme an, es geht hier um Defaultargumente bei Funktionsaufrufen. Die befinden sich lexikalisch an anderer Stelle, müssen aber nat. bei der Auswertung berücksichtigt werden.


  • Mod

    Ein anderer Fall ist der Aufruf von Memberfunktionen auf Temporaries.

    struct Foo
    {
        int foo() { /* ... */ }
    };
    
    int main()
    {
        Foo().foo();
    }
    

    Der Objektparameter wird hier nicht an einen Referenz gebunden. Trotzdem kann kein Zweifel bestehen, dass das Temporary lang genug lebt für den Aufruf der Memberfunktion (und es existiert auch keine spezielle Lebensverlängerungsregel). Das ist z.B. auch hier nicht das Problem: Use of full expression lvalue before temporary destruction
    Gäbe es dabei Zweifel, käme die Frage nach der Konvertierung in bool wegen des if-Statements gar nicht erst auf.



  • camper schrieb:

    Richtig, Das gesamte Konstrukt (einzelne Ausdrücke plus Kommas) ist kein Ausdruck, sondern eine Parameter- oder Initialisierungsliste.

    Erstmal ist das trotzdem ein Ausdruck. Und zweitens hast du ja selbst "For example" hervorgehoben. Es sind also Beispiele. Von einer Konkretisierung kann hier keine Rede sein. Zumal ich hier auch nicht sehe, dass es sich explizit um Initialisierungslisten handelt. Hier wird vielmehr allgemein von Initialisierungen in Verbindung mit Konstruktoraufrufen gesprochen.

    groovemaster schrieb:

    Wenn du mir den Paragrafen nennst

    1.9-14

    groovemaster schrieb:

    schau ich nochmal nach, aber ich nehme an, es geht hier um Defaultargumente bei Funktionsaufrufen.

    Ist wieder nur als Beispiel aufgeführt. Von einer Konkretisierung (siehe oben) kann also keine Rede sein.

    camper schrieb:

    Ein anderer Fall [...]

    Hat mit dem Problem hier nichts zu tun. Es geht ausschliesslich um die Übergabe von Parametern.


  • Mod

    groovemaster schrieb:

    camper schrieb:

    Richtig, Das gesamte Konstrukt (einzelne Ausdrücke plus Kommas) ist kein Ausdruck, sondern eine Parameter- oder Initialisierungsliste.

    Erstmal ist das trotzdem ein Ausdruck.

    Es passt allerdings nicht zur Definition in Abschnitt 5

    An expression is a
    sequence of operators and operands that specifies a computation.

    Das Komma hier ist kein Operator und ein Operand sowieso nicht.

    Und zweitens hast du ja selbst "For example" hervorgehoben. Es sind also Beispiele. Von einer Konkretisierung kann hier keine Rede sein.

    Es ist ist jedenfalls in Hinblick auf das Beispiel selbst konkret.

    Zumal ich hier auch nicht sehe, dass es sich explizit um Initialisierungslisten handelt. Hier wird vielmehr allgemein von Initialisierungen in Verbindung mit Konstruktoraufrufen gesprochen.

    stimmt. hab ich etwas oberflächlich gelesen.

    groovemaster schrieb:

    Ist wieder nur als Beispiel aufgeführt. Von einer Konkretisierung (siehe oben) kann also keine Rede sein.

    Das kann auch nur ein Mittel sein, um Widersprüche zu vermeiden, indem hier nicht gesagt wird, dass diese Aufzählung durch Beispiele vollständig ist. Es wird aber auch nicht ausgeschlossen. Zumindest erlaubt das Beispiel, gewisse Rückschlüsse, was mit den vorgenannten subexpressions gemeint ist. Aber irgendwie kommen wir hier vom eigentlichen Thema ab.

    camper schrieb:

    Ein anderer Fall [...]

    Hat mit dem Problem hier nichts zu tun. Es geht ausschliesslich um die Übergabe von Parametern.

    Mir geht es um die Lebenszeit von Temporaries. Im Übrigen ist auch ein Objektparameter ein Parameter.

    Eine Zitat von Stroustrup ist natürlich nicht normativ, passt hier aber exakt, in §10.4.10 TCPL bringt er folgendes Beispiel:

    void f(string& s1, string& s2, string& s3)
    {
        const char* cs = (s1+s2).c_str();
        cout << cs;
    
        if (strlen(cs=(s2+s3).c_str())<8 && cs[0]=='a') {
            // cs used here
        }
    }
    

    und dann folgendend:

    The example with the if-statement is a bit more subtle. The condition will work as expected because the full expression in which the temporary holding s2+s3 is created is the condition itself. However, that temporary is destroyed before the controlled statement is entered, so any use of cs there is not guaranteed to work.


Anmelden zum Antworten