Menangkap objek yang baru dibangun oleh perilaku konstan yang tidak ditentukan

11

Apakah yang berikut (contoh yang dibuat-buat) baik-baik saja atau apakah itu perilaku tidak terdefinisi:

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

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

Jawaban:

12

Itu aman. Konstitusi memperpanjang umur sementara. Ruang lingkup akan menjadi ruang lingkup const ref.

Masa pakai objek sementara dapat diperpanjang dengan mengikat ke referensi nilai konstan atau ke nilai referensi (karena C ++ 11), lihat inisialisasi referensi untuk detail.

Setiap kali referensi terikat untuk sementara atau ke sub-proyeknya, masa pakai sementara diperpanjang untuk mencocokkan masa pakai referensi, dengan pengecualian berikut :

  • terikat sementara untuk nilai kembali suatu fungsi dalam pernyataan kembali tidak diperpanjang: itu dihancurkan segera di akhir ekspresi kembali. Fungsi seperti itu selalu mengembalikan referensi yang menggantung.
  • terikat sementara untuk anggota referensi dalam daftar penginisialisasi konstruktor hanya bertahan sampai konstruktor keluar, tidak selama objek ada. (catatan: inisialisasi seperti itu terbentuk dengan buruk pada DR 1696).
  • sementara terikat ke parameter referensi dalam panggilan fungsi ada sampai akhir ekspresi penuh berisi panggilan fungsi: jika fungsi mengembalikan referensi, yang hidup lebih lama dari ekspresi penuh, itu menjadi referensi menggantung.
  • terikat sementara pada referensi di penginisialisasi yang digunakan dalam ekspresi-baru ada sampai akhir ekspresi penuh yang mengandung ekspresi-baru, tidak selama objek diinisialisasi. Jika objek yang diinisialisasi bertahan lebih lama dari ekspresi penuh, anggota rujukannya menjadi referensi yang menggantung.
  • sementara terikat pada referensi dalam elemen referensi agregat diinisialisasi menggunakan sintaks inisialisasi langsung (tanda kurung) yang bertentangan dengan sintaksis inisialisasi daftar (kurung) ada sampai akhir ekspresi penuh berisi inisialisasi. struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference

Secara umum, masa temporer tidak dapat diperpanjang dengan "meneruskannya": referensi kedua, yang diinisialisasi dari referensi dimana temporer terikat, tidak mempengaruhi masa pakainya.

seperti yang ditunjukkan @Konrad Rudolph (dan lihat paragraf terakhir di atas):

"Jika c.GetSomeVariable()mengembalikan referensi ke objek lokal atau referensi yang itu sendiri memperpanjang masa hidup beberapa objek, ekstensi seumur hidup tidak masuk"

Pelupaan
sumber
1
Anda harus mengutip sumber kutipan itu.
Lightness Races in Orbit
@LightnessRaceswithMonica selesai. Saya mencari teks yang lebih baik.
Diabaikan
2
Akan bagus untuk menggarisbawahi bahwa ini hanya berlaku untuk nilai . Jika c.GetSomeVariable()mengembalikan referensi ke objek lokal atau referensi yang itu sendiri memperpanjang masa hidup beberapa objek, ekstensi seumur hidup tidak masuk.
Konrad Rudolph
@KonradRudolph Terima kasih! Saya menambahkan pengecualian juga.
Diabaikan
4

Seharusnya tidak ada masalah di sini, berkat perpanjangan seumur hidup . Objek yang baru dibangun akan bertahan sampai referensi keluar dari ruang lingkup.

Brian
sumber
3

Ya ini benar-benar aman: pengikatan constreferensi memperpanjang umur sementara ke lingkup referensi itu.

Perhatikan bahwa perilaku tersebut tidak transitif . Misalnya dengan

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

cc menggantung.

Batsyeba
sumber
2

Ini aman.

[class.temporary]/5: Ada tiga konteks di mana temporaries dihancurkan pada titik yang berbeda dari akhir ekspresi penuh . [..]

[class.temporary]/6: Konteks ketiga adalah ketika referensi terikat ke objek sementara. Objek sementara yang menjadi acuan referensi atau objek temporer yang merupakan objek lengkap dari suatu sub-objek di mana referensi terikat bertahan selama masa referensi jika glvalue yang menjadi acuan referensi diperoleh melalui salah satu dari berikut ini : [banyak hal di sini]

Lightness Races di Orbit
sumber
1

Aman dalam kasus khusus ini. Namun perhatikan bahwa tidak semua temporer aman untuk ditangkap dengan referensi const ... misalnya

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

Referensi yang diperoleh zBUKAN aman untuk digunakan karena instance sementara akan dihancurkan pada akhir ekspresi penuh, sebelum mencapai printfpernyataan. Output adalah:

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