std :: pair内の初期化子リスト


26

このコード:

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

コンパイルしますが、segfaultを返します。どうして?

gcc 8.3.0とオンラインコンパイラでテストされています。


1
便宜上:Godboltは、リンクしてなくて std::pair
Max Langhof

回答:


24

std::initializer_list格納することを意図したものではなく、単に...初期化のためのものです。内部的には、最初の要素へのポインタとサイズを格納するだけです。コードでは、std::stringオブジェクトは一時的なものであり、initializer_listどちらもオブジェクトの所有権を取得したり、オブジェクトの寿命を延ばしたり、オブジェクトをコピーしたりしないため(コンテナーではないため)、作成後すぐにスコープから外れますが、オブジェクトinitializer_listへのポインターは保持されます。そのため、セグメンテーション違反が発生します。

保存には、std::vectorまたはのようなコンテナを使用する必要がありますstd::array


これはコンパイル可能だと私は気にしています。愚かな言葉:(
オービットの軽さの

1
@LightnessRaceswithMonica私はビーフをたくさん持っていinitializer_listます。move-onlyオブジェクトを使用することはできないため、たとえば、unique_ptrのベクターでlist initを使用することはできません。のサイズはinitializer_listコンパイル時の定数ではありません。そして、完全に異なることstd::vector<int>(3)std::vector<int>{3}行うという事実。私を悲しくさせます:(
bolov


3

もう少し詳細を追加します。基になる配列はstd::initializer_list、一時的なものと同様に動作します。次のクラスについて考えてみます。

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

次のコードでのその使用法:

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

プリントアウト

ctor
dtor
barrier

最初の行から、型の一時的なインスタンスXが作成され(コンストラクタをから変換することにより1)、同様に破棄されます。に格納された参照pはぶら下がります。

についてはstd::initializer_list、次のように使用すると、

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

次に、基になる(一時的な)配列が存在する限り存在しlます。したがって、出力は次のようになります。

ctor
ctor
barrier
dtor
dtor

ただし、

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

出力は再びです

ctor
dtor
barrier

基礎となる(一時的な)配列は最初の行にのみ存在するためです。lthen の要素へのポインタを逆参照すると、未定義の動作が発生します。

ライブデモはこちらです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.