Erfasst ein neu erstelltes Objekt durch const ref undefiniertes Verhalten

11

Ist das folgende (erfundenes Beispiel) in Ordnung oder ist es undefiniertes Verhalten:

// undefined behavior?
const auto& c = SomeClass{};

// use c in code later
const auto& v = c.GetSomeVariable();
Samaursa
quelle

Antworten:

12

Es ist sicher. Const ref verlängert die Lebensdauer von temporären. Der Geltungsbereich ist der Geltungsbereich von const ref.

Die Lebensdauer eines temporären Objekt kann durch Bindung an eine const lvalue Referenz oder auf eine R - Wert - Referenz (da C ++ 11) verlängert werden, siehe Bezug Initialisierung für weitere Einzelheiten.

Immer wenn eine Referenz an ein temporäres Objekt oder an ein Unterobjekt davon gebunden ist, wird die Lebensdauer des temporären Objekts mit den folgenden Ausnahmen verlängert, um der Lebensdauer der Referenz zu entsprechen :

  • Eine temporäre Bindung an einen Rückgabewert einer Funktion in einer return-Anweisung wird nicht erweitert: Sie wird sofort am Ende des return-Ausdrucks zerstört. Eine solche Funktion gibt immer eine baumelnde Referenz zurück.
  • Eine temporäre Bindung an ein Referenzelement in einer Konstruktorinitialisiererliste bleibt nur so lange bestehen, bis der Konstruktor beendet wird, nicht solange das Objekt vorhanden ist. (Hinweis: Eine solche Initialisierung ist ab DR 1696 schlecht ausgebildet.)
  • Eine temporäre Bindung an einen Referenzparameter in einem Funktionsaufruf besteht bis zum Ende des vollständigen Ausdrucks, der diesen Funktionsaufruf enthält: Wenn die Funktion eine Referenz zurückgibt, die den vollständigen Ausdruck überlebt, wird sie zu einer baumelnden Referenz.
  • Eine temporäre Bindung an eine Referenz im Initialisierer, die in einem neuen Ausdruck verwendet wird, ist bis zum Ende des vollständigen Ausdrucks vorhanden, der diesen neuen Ausdruck enthält, nicht solange das initialisierte Objekt. Wenn das initialisierte Objekt den vollständigen Ausdruck überlebt, wird sein Referenzelement zu einer baumelnden Referenz.
  • Bis zum Ende des vollständigen Ausdrucks, der den Initialisierer enthält, ist eine temporäre Bindung an eine Referenz in einem Referenzelement eines Aggregats vorhanden, das mit der Direktinitialisierungssyntax (Klammern) im Gegensatz zur Listeninitialisierungssyntax (geschweifte Klammern) initialisiert wurde. struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

Im Allgemeinen kann die Lebensdauer eines Temporärs nicht durch "Weitergabe" weiter verlängert werden: Eine zweite Referenz, die aus dem Verweis initialisiert wurde, an den das Temporär gebunden war, hat keinen Einfluss auf seine Lebensdauer.

wie @Konrad Rudolph betonte (und siehe den letzten Absatz von oben):

"Wenn c.GetSomeVariable()eine Referenz auf ein lokales Objekt oder eine Referenz zurückgegeben wird, die selbst die Lebensdauer eines Objekts verlängert, wird die Lebensdauerverlängerung nicht aktiviert."

Vergessenheit
quelle
1
Sie sollten die Quelle dieses Zitats angeben.
Leichtigkeitsrennen im Orbit
@LightnessRaceswithMonica fertig. Ich suchte nach einem besseren Text.
Vergessenheit
2
Es wäre gut zu betonen, dass dies nur für Werte gilt . Wenn c.GetSomeVariable()eine Referenz auf ein lokales Objekt oder eine Referenz zurückgegeben wird, die selbst die Lebensdauer eines Objekts verlängert, wird die Lebensdauerverlängerung nicht
Konrad Rudolph
@KonradRudolph Danke! Ich habe auch die Ausnahme hinzugefügt.
Vergessenheit
4

Dank der Verlängerung der Lebensdauer sollte hier kein Problem auftreten . Das neu erstellte Objekt bleibt bestehen, bis die Referenz den Gültigkeitsbereich verlässt.

Brian
quelle
3

Ja, das ist absolut sicher: Die Bindung an eine constReferenz verlängert die Lebensdauer der temporären Referenz auf den Umfang dieser Referenz.

Beachten Sie, dass das Verhalten jedoch nicht transitiv ist . Zum Beispiel mit

const auto& cc = []{
    const auto& c = SomeClass{};
    return c;
}();

cc baumelt.

Bathseba
quelle
2

Das ist sicher.

[class.temporary]/5: Es gibt drei Kontexte, in denen Provisorien an einem anderen Punkt als am Ende des vollständigen Ausdrucks zerstört werden . [..]

[class.temporary]/6: Der dritte Kontext ist, wenn eine Referenz an ein temporäres Objekt gebunden ist. Das temporäre Objekt, an das die Referenz gebunden ist, oder das temporäre Objekt, das das vollständige Objekt eines Unterobjekts ist, an das die Referenz gebunden ist, bleibt für die Lebensdauer der Referenz bestehen, wenn der Wert, an den die Referenz gebunden ist, durch eine der folgenden Methoden erhalten wurde : [viele Dinge hier]

Leichtigkeitsrennen im Orbit
quelle
1

In diesem speziellen Fall ist es sicher. Beachten Sie jedoch, dass nicht alle Provisorien sicher durch konstante Referenz erfasst werden können ... zum Beispiel

#include <stdio.h>

struct Foo {
    int member;

    Foo() : member(0) {
        printf("Constructor\n");
    }

    ~Foo() {
        printf("Destructor\n");
    }

    const Foo& method() const {
        return *this;
    }
};

int main() {
    {
        const Foo& x = Foo{};        // safe
        printf("here!\n");
    }
    {
        const int& y = Foo{}.member; // safe too (special rule for this)
        printf("here (2)!\n");
    }
    {
        const Foo& z = Foo{}.method(); // NOT safe
        printf("here (3)!\n");
    }
    return 0;
}

Die für erhaltene Referenz zist NICHT sicher zu verwenden, da die temporäre Instanz am Ende des vollständigen Ausdrucks zerstört wird, bevor die printfAnweisung erreicht wird. Ausgabe ist:

Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!
6502
quelle