Przechwytuje nowo skonstruowany obiekt przez stałe zachowanie niezdefiniowane


11

Czy następujący (wymyślony przykład) jest w porządku, czy jest to niezdefiniowane zachowanie:

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

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

Odpowiedzi:


12

To jest bezpieczne. Const ref przedłuża czas życia tymczasowego. Zakres będzie zakresem stałej ref.

Życia tymczasowego obiektu może być przedłużony przez wiązanie z odniesieniem const lwartości lub odnośnik RValue (od C ++, 11), patrz inicjalizacji odniesienia dla szczegółów.

Ilekroć odniesienie jest powiązane z tymczasowym lub jego podobiektywem, jego żywotność jest przedłużana, aby pasowała do żywotności referencyjnego, z następującymi: wyjątkami :

  • tymczasowe powiązanie z wartością zwrotną funkcji w instrukcji return nie jest przedłużane: jest niszczone natychmiast na końcu wyrażenia return. Taka funkcja zawsze zwraca zwisające odwołanie.
  • tymczasowe powiązanie z elementem referencyjnym na liście inicjalizatora konstruktora trwa tylko do momentu wyjścia konstruktora, dopóki obiekt nie istnieje. (uwaga: taka inicjalizacja jest źle sformułowana od DR 1696).
  • tymczasowe powiązanie z parametrem odwołania w wywołaniu funkcji istnieje do końca pełnego wyrażenia zawierającego to wywołanie funkcji: jeśli funkcja zwróci odwołanie, które przeżywa pełne wyrażenie, staje się odwołaniem wiszącym.
  • tymczasowe powiązanie z odwołaniem w inicjalizatorze zastosowanym w nowym wyrażeniu istnieje do końca pełnego wyrażenia zawierającego to nowe wyrażenie, nie tak długo jak zainicjowany obiekt. Jeśli zainicjowany obiekt przeżywa pełne wyrażenie, jego element odniesienia staje się odwołaniem wiszącym.
  • tymczasowe powiązanie z referencją w elemencie referencyjnym agregatu zainicjowanego przy użyciu składni inicjalizacji bezpośredniej (nawiasy) w przeciwieństwie do składni inicjalizacji listy (nawiasy klamrowe) istnieje do końca pełnego wyrażenia zawierającego inicjator. struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

Zasadniczo czas życia tymczasowego nie może być dalej przedłużany przez „przekazanie go”: drugie odniesienie, zainicjowane z odniesienia, z którym związany był tymczasowy, nie wpływa na jego żywotność.

jak zauważył @Konrad Rudolph (i patrz ostatni akapit powyżej):

„Jeśli c.GetSomeVariable()zwraca odwołanie do obiektu lokalnego lub odwołanie, że sam przedłuża żywotność jakiegoś obiektu, rozszerzenie życia nie uruchamia się”


1
Powinieneś przytoczyć źródło tego cytatu.
Wyścigi lekkości na orbicie

@LightnessRaceswithMonica zrobione. Szukałem lepszego tekstu.
Oblivion,

2
Warto podkreślić, że dotyczy to tylko wartości . Jeśli c.GetSomeVariable()zwraca odwołanie do obiektu lokalnego lub odniesienie, że sam przedłuża czas życia jakiegoś obiektu, przedłużenie czasu życia nie jest uruchamiane.
Konrad Rudolph

@KonradRudolph Thanks! Dodałem też wyjątek.
Oblivion,


3

Tak, jest to całkowicie bezpieczne: powiązanie z constodniesieniem wydłuża czas jego użytkowania do zakresu tego odniesienia.

Zauważ, że zachowanie nie jest przechodnie . Na przykład za pomocą

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

cc wisi.


2

To jest bezpieczne.

[class.temporary]/5: Istnieją trzy konteksty, w których elementy tymczasowe są niszczone w innym punkcie niż koniec pełnego wyrażenia . [..]

[class.temporary]/6: Trzeci kontekst dotyczy powiązania odwołania z obiektem tymczasowym. Obiekt tymczasowy, z którym powiązane jest odniesienie, lub obiekt tymczasowy, który jest kompletnym obiektem podobiektu, z którym powiązane jest odniesienie, utrzymuje się przez cały okres istnienia odniesienia, jeśli wartość glvalue, z którą powiązane jest odniesienie, uzyskano w jeden z następujących sposobów : [wiele rzeczy tutaj]


1

W tym konkretnym przypadku jest bezpieczny. Zauważ jednak, że nie wszystkie tymczasowe są bezpieczne na przykład na podstawie stałej referencji ...

#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;
}

Odwołanie uzyskane dla zNIE jest bezpieczne w użyciu, ponieważ tymczasowa instancja zostanie zniszczona pod koniec pełnego wyrażenia, przed osiągnięciem printfinstrukcji. Dane wyjściowe to:

Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.