Perilaku yang tidak terdefinisi dalam vektor pemeran vektor

19

Mengapa kode ini menulis sejumlah bilangan bulat yang tampaknya tidak diinisialisasi?

#include <iostream>
#include <vector>
using namespace std;


int main()
{
    for (int i : vector<vector<int>>{{77, 777, 7777}}[0])
        cout << i << ' ';
}

Saya mengharapkan hasilnya 77 777 7777.

Apakah kode ini seharusnya tidak terdefinisi?

GT 77
sumber

Jawaban:

18

vector<vector<int>>{{77, 777, 7777}}bersifat sementara dan kemudian digunakan vector<vector<int>>{{77, 777, 7777}}[0]dalam rentang-untuk akan menjadi perilaku yang tidak terdefinisi.

Anda harus membuat variabel terlebih dahulu, seperti

#include <iostream>
#include <vector>
using namespace std;


int main()
{
    auto v = vector<vector<int>>{{77, 777, 7777}};
    for(int i: v[0])
        cout << i << ' ';
}

Juga Jika Anda menggunakan Dentang 10.0.0 itu memberi peringatan tentang perilaku ini.

peringatan: objek yang mendukung pointer akan dihancurkan pada akhir vektor ekspresi penuh [-Wdangling-gsl]> {{77, 777, 7777}} [0]

Gaurav Dhiman
sumber
2
Silakan gunakan using std::vectoralih-alih using namespace std;untuk mencegah praktik buruk ini menyebar.
infinitezero
10

Ini karena vektor yang Anda iterasi akan dihancurkan sebelum memasuki loop.

Inilah yang biasanya terjadi:

auto&& range = vector<vector<int>>{{77, 777, 7777}}[0];
auto&& first = std::begin(range);
auto&& last = std::end(range);
for(; first != last; ++first)
{
    int i = *first;
    // the rest of the loop
}

Masalahnya dimulai pada baris pertama karena dievaluasi sebagai berikut:

  1. Pertama-tama buatlah vektor vektor dengan argumen yang diberikan dan vektor itu menjadi sementara karena tidak memiliki nama.

  2. Kemudian referensi rentang terikat ke vektor yang disandikan yang hanya akan valid selama vektor yang memuatnya valid.

  3. Setelah titik koma tercapai, vektor sementara dihancurkan dan dalam destruktornya ia akan menghancurkan dan membatalkan alokasi semua vektor yang tersimpan yang mencakup yang disandikan.

  4. Anda berakhir dengan referensi ke vektor hancur yang akan diiterasi.

Untuk menghindari masalah ini ada dua solusi:

  1. Deklarasikan vektor sebelum loop sehingga ia bertahan sampai cakupannya berakhir yang mencakup loop.

  2. C ++ 20 datang dengan pernyataan init yang disediakan untuk menyelesaikan masalah ini dan lebih baik daripada pendekatan pertama jika Anda ingin vektor dihancurkan setelah loop segera:

    for (vector<vector<int>> vec{{77, 777, 7777}}; int i : vec[0])
    {
    }
dev65
sumber
Ini bukan apa yang "biasanya" terjadi. Perilaku yang tepat ini (ditambah pelingkupan yang tepat dan pertimbangan tentang penamaan) diamanatkan oleh standar, tunduk pada aturan as-if.
Konrad Rudolph
Saya mengacu pada masa hidup. Bahkan jika Anda menulis kode yang sama dengan tangan, Anda hanya memiliki jaminan bahwa Anda mendapatkan perilaku yang diinginkan dan kompiler akan melakukan apa yang dapat dilakukan dengan optimasi
dev65
TIL C ++ 20 rentang mendeklarasikan-untuk sintaks. Tidak yakin apakah akan bahagia atau sedih.
Asteroids With Wings
6
vector<vector<int>>{{77, 777, 7777}}[0]

Saya berharap ini menjuntai.

Meskipun definisi rentang-untuk memastikan bahwa RHS usus besar tetap "hidup" selama jangka waktu, Anda masih berlangganan sementara. Hanya hasil dari subskrip disimpan, tetapi hasil itu adalah referensi, dan vektor yang sebenarnya tidak dapat bertahan melewati ekspresi penuh yang dinyatakannya. Itu tidak menggambarkan keseluruhan loop.

Asteroid Dengan Sayap
sumber