Захоплює щойно побудований об’єкт const ref невизначеною поведінкою


11

Чи нормально таке (надуманий приклад) чи це невизначена поведінка:

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

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

Відповіді:


12

Це безпечно. Const ref продовжує термін експлуатації тимчасових. Областю застосування буде сфера const ref.

Термін експлуатації тимчасового об'єкта може бути продовжений шляхом прив'язки до посилання const lvalue або до посилання rvalue (оскільки C ++ 11), див. Ініціалізацію посилань .

Щоразу, коли посилання прив'язується до тимчасового або до його суб'єкта, термін дії тимчасового подовжується, щоб відповідати терміну експлуатації посилання, за такими винятками :

  • тимчасове прив’язане до повернення значення функції у зворотному операторі не розширюється: воно руйнується негайно в кінці виразу повернення. Така функція завжди повертає звисаючу посилання.
  • тимчасове прив’язання до посилального члена у списку ініціалізатора конструктора зберігається лише до тих пір, поки конструктор не вийде, не доки об'єкт існує. (зауважте: така ініціалізація неправильно сформована як в DR 1696).
  • тимчасовий прив'язок до опорного параметра у виклику функції існує до кінця повного виразу, що містить цей виклик функції: якщо функція повертає посилання, яке переживає повний вираз, воно стає звисаючим посиланням.
  • тимчасова прив'язка до посилання в ініціалізаторі, що використовується в новому виразі, існує до кінця повного виразу, що містить цей новий вираз, не так довго, як ініціалізований об'єкт. Якщо ініціалізований об'єкт переживає повний вираз, його референтний член стає звисаючим посиланням.
  • тимчасове прив’язання до посилання в еталонному елементі сукупності, ініціалізованого за допомогою синтаксису прямої ініціалізації (дужки) на відміну від синтаксису (дужки) списку-ініціалізації існує до кінця повного виразу, що містить ініціалізатор. struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

Загалом, термін служби тимчасового не може бути продовжений шляхом "передачі його": друга посилання, ініціалізована з посилання, до якого тимчасовий прив'язується, не впливає на її тривалість життя.

як зазначав @Konrad Rudolph (і див. останній абзац вище):

"Якщо c.GetSomeVariable()повертає посилання на локальний об'єкт або посилання на те, що він сам подовжує термін дії якогось об'єкта, продовження терміну дії не починається"


1
Ви повинні навести джерело цієї цитати.
Гонки легкості по орбіті

@LightnessRaceswithMonica виконано. Я шукав кращого тексту.
Забуття

2
Добре було б підкреслити, що це стосується лише цінностей . Якщо c.GetSomeVariable()повертає посилання на локальний об'єкт або посилання , що воно саме продовження терміну експлуатації якого - то об'єкта, збільшення терміну експлуатації ніяк НЕ загнутися.
Konrad Rudolph

@KonradRudolph Дякую! Я також додав виняток.
Забуття


3

Так, це абсолютно безпечно: прив'язка до constпосилання подовжує термін служби тимчасового в межах цієї посилання.

Зауважте, що поведінка не є транзитивною . Наприклад, с

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

cc звисає.


2

Це безпечно.

[class.temporary]/5: Існує три контексти, в яких темпорації руйнуються в іншій точці, ніж в кінці повного виразу . [..]

[class.temporary]/6: Третій контекст - це посилання на тимчасовий об'єкт. Тимчасовий об'єкт, до якого пов'язується посилання, або тимчасовий об'єкт, який є повним об'єктом суб'єкта, до якого посилається прив'язка, зберігається протягом життя посилання, якщо glvalue, до якого прив'язується посилання, отримано за допомогою одного з наступних : [тут багато речей]


1

У цьому конкретному випадку це безпечно. Однак зауважте, що не всі часові сховища безпечні для зйомки за допомогою посилання const ... наприклад

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

Посилання, отримане для z, НЕ БЕЗПЕЧНЕ для використання, оскільки тимчасовий екземпляр буде знищений в кінці повного виразу, перш ніж дійти до printfзаяви. Вихід:

Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.