Std :: pair içindeki başlatıcı listesi


26

Bu kod:

#include <iostream>
#include <string>

std::pair<std::initializer_list<std::string>, int> groups{ { "A", "B" }, 0 };

int main()
{
    for (const auto& i : groups.first)
    {
        std::cout << i << '\n';
    }
    return 0;
}

derler ancak segfault döndürür. Neden?

Gcc 8.3.0 ve çevrimiçi derleyiciler üzerinde test edilmiştir.


1
Kolaylık sağlamak için: Godbolt ile ve olmadan bağlantı kurarstd::pair .
Max Langhof

Yanıtlar:


24

std::initializer_listdepolanması değil, sadece ... iyi başlatılması içindir. Dahili olarak sadece ilk öğeye ve boyuta bir işaretçi saklar. Kodunuzda std::stringnesneler geçicidir ve initializer_listikisi de sahiplenmez, ikisi de yaşamlarını uzatmaz, ikisi de kopyalamaz (çünkü bir kap değil), bu yüzden yaratımdan hemen sonra kapsam dışına çıkarlar, ancak initializer_listhala onlara bir işaretçi tutarlar. Bu yüzden segmentasyon hatası alıyorsunuz.

Depolama için std::vectorveya gibi bir kap kullanmalısınız std::array.


Bunun derlenebilir olması beni rahatsız ediyor. Aptal dil :(
Yörünge'de Hafiflik Yarışları

1
@LightnessRaceswithMonica Çok fazla sığır eti var initializer_list. Yalnızca hareket eden nesneler kullanmak mümkün değildir, bu nedenle list init öğesini örneğin unique_ptr vektörü ile kullanamazsınız. Boyutu initializer_listderleme zamanı sabiti değildir. Ve gerçeği std::vector<int>(3)ve std::vector<int>{3}tamamen farklı şeyler yapmak. Beni üzüyor :(
bolov


3

Sadece biraz daha ayrıntı eklerdim. Altta yatan bir dizi, std::initializer_listgeçici olarak benzer şekilde davranır. Aşağıdaki sınıfı düşünün:

struct X
{
   X(int i) { std::cerr << "ctor\n"; }
   ~X() { std::cerr << "dtor\n"; }
};

ve aşağıdaki kodda kullanımı:

std::pair<const X&, int> p(1, 2);
std::cerr << "barrier\n";

Çıktı

ctor
dtor
barrier

çünkü ilk satırda geçici bir tür örneği Xoluşturulur (yapıcıyı dönüştürerek 1) ve yok eder. Saklanan referans pdaha sonra sarkar.

Gelince std::initializer_list, bu şekilde kullanırsanız:

{
   std::initializer_list<X> l { 1, 2 };
   std::cerr << "barrier\n";
}

ardından, altta yatan (geçici) dizi, lçıkışlar olduğu sürece mevcuttur . Bu nedenle, çıktı:

ctor
ctor
barrier
dtor
dtor

Ancak,

std::pair<std::initializer_list<X>, int> l { {1}, 2 };
std::cerr << "barrier\n";

Çıktı yine

ctor
dtor
barrier

çünkü temel (geçici) dizi sadece ilk satırda bulunur. İmlecin öğelerinin kaydının silinmesi, ltanımlanmamış davranışla sonuçlanır.

Canlı demo burada .

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.