Dökülen vektörlerin vektöründe tanımlanmamış davranış


19

Bu kod neden tanımsız görünen, başlatılmamış bir tam sayı yazıyor?

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


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

Çıktının olmasını bekliyordum 77 777 7777.

Bu kodun tanımsız olması gerekiyor mu?

Yanıtlar:


18

vector<vector<int>>{{77, 777, 7777}}geçici ve sonra vector<vector<int>>{{77, 777, 7777}}[0]ranged-for kullanmak tanımsız bir davranış olacaktır.

Önce bir değişken oluşturmalısınız.

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


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

Ayrıca Clang 10.0.0 kullanırsanız, bu davranış hakkında uyarı verir.

uyarı: işaretçiyi destekleyen nesne, tam ifade [-Wdangling-gsl] vektörü> {{77, 777, 7777}} [0] sonunda yok edilecek


2
Bu kötü uygulamanın yayılmasını önlemek için lütfen using std::vectoryerine kullanın using namespace std;.
infinitezero

10

Bunun nedeni, yinelemekte olduğunuz vektörün döngüye girmeden önce yok edilmesidir.

Genellikle böyle olur:

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
}

Sorunlar ilk satırda başlar çünkü aşağıdaki gibi değerlendirilir:

  1. Öncelikle verilen vektörlerle vektörlerin vektörünü oluşturun ve bu vektörün adı olmadığı için geçici olur.

  2. Daha sonra aralık referansı, yalnızca onu içeren vektör geçerli olduğu sürece geçerli olacak abonelikli vektöre bağlanır.

  3. Noktalı virgül ulaşıldığında geçici vektör yok edilir ve yıkıcısında, abone olanı içeren saklı vektörleri yok eder ve yeniden konumlandırır.

  4. Sonunda, tekrarlanacak olan yok edilmiş bir vektörün referansıyla sonuçlanırsınız.

Bu sorunu önlemek için iki çözüm vardır:

  1. Vektörü döngüden önce duyurun, böylece döngüyü içeren kapsamı bitene kadar devam eder.

  2. C ++ 20, bu sorunları çözmek için sağlanan bir init deyimi ile birlikte gelir ve vektörün hemen döngüden sonra yok edilmesini istiyorsanız ilk yaklaşımdan daha iyidir:

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

"Tipik" olan bu değildir. Bu kesin davranış (artı uygun kapsam belirleme ve adlandırma ile ilgili hususlar), standart kurallara uygun olarak, standart tarafından zorunlu kılınmıştır.
Konrad Rudolph

Yaşamlara atıfta bulunuyorum. Aynı kodu elle yazsanız bile, sadece istediğiniz davranışı elde edeceğinizi garanti edersiniz ve derleyici optimizasyonlarla
elinden geleni yapacaktır

TIL C ++ 20 sözdizimi için bildirme aralığı. Mutlu ya da üzgün olup olmadığından emin değilim.
Kanatlı Asteroitler

6
vector<vector<int>>{{77, 777, 7777}}[0]

Bunun sarkık olmasını bekliyorum.

Aralıklı bir tanım, kolonun RHS'sinin bu süre boyunca "canlı" kalmasını sağlar, ancak yine de geçici bir aboneliğiniz vardır. Yalnızca alt simge sonucu tutulur, ancak bu sonuç bir referanstır ve gerçek vektör bildirildiği tam ifadenin ötesinde hayatta kalamaz . Bu, tüm döngüyü tanımlamaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.