web-dev-qa-db-ja.com

ペアの配列の初期化にC ++ 14で二重中括弧が必要なのはなぜですか?

C++ 14標準では、std::arrayの初期化は単一の中括弧で行うことができます( http://en.cppreference.com/w/cpp/container/array を参照):

ただし、これはstd::arraystd::pairでは機能しません。

これらが機能する理由:

std::pair<int, int> p { 1, 2 };
std::array<int, 3> a {1, 2, 3};

しかし、これはnot動作しません:

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

これは再び機能しますか?

std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};

また、完了のために、古き良き配列の初期化は単一の中括弧で機能します

std::pair<int, int> c[3] {{1, 11}, {2, 22}, {3, 33}};
62
Chiel

これは、有名な 最も厄介な解析 に多少似た解析の曖昧さのようです。何が起こっているのだろうと思う:

書くなら

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

コンパイラには、構文を解釈する2つの方法があります。

  1. 完全なブレースの初期化を実行します(最も外側のブレースはstd::arrayの集約初期化を指し、最初の最も内側のブレースは実際のC配列であるstd::arrayの内部メンバー表現を初期化します)。 std::pair<int, int>はその後1で初期化できないため、これはコンパイルに失敗します(中括弧はすべて使い果たされます)。 clangは、次のことを正確に示すコンパイラエラーを返します。

    error: no viable conversion from 'int' to 'std::pair<int, int>'
     std::array<std::pair<int, int>, 3> a{{1, 11}, {2, 22}, {3, 33}};
                                              ^
    

    また、初期化される内部メンバー集合体がない場合、この問題は解決されることに注意してください。

    std::pair<int, int> b[3] = {{1, 11}, {2, 22}, {3, 33}};
    

    集計の初期化として問題なくコンパイルされます。

  2. (意図したとおりです。)ブレースを省略した初期化を実行します。したがって、最も内側のブレースは個々のペアの集計初期化用であり、内部配列表現のブレースは省略されます。 rustyx's answer で正しく指摘されているように、この曖昧さがなかったとしても、std::pairは集約型ではないため、ブレース省略の規則は適用されず、プログラムは不正な形式。

コンパイラーはオプション1を優先します。追加の波括弧を提供することにより、波括弧の初期化を実行し、構文のあいまいさを解消します。

30
Jodocus

C++ 14 ブレース省略規則 は、サブ集計の初期化にのみ適用されます。

たとえば、次のようなものが機能します。

std::array<std::array<int, 3>, 3> a{1, 11, 2, 22, 3, 33};

ここでは、追加のブレースなしで、集計の集計をリスト初期化できます。

ただし、std::pairaggregate (コンストラクターを含む)ではないため、ルールは適用されません。

つまり、ブレース省略規則がない場合、std::arrayは、それ自体が内部に配列を持つ集合体であり、list-initialized。クラステンプレートarrayは次のように実装されることに注意してください。

template<typename T, std::size_t N> 
struct array {
  T elems[N];
};

中括弧の省略規則を使用せずにlist-initializeするには、elemsメンバーに到達するための追加のブレースセットが必要です。

14
rustyx

理論的には、std::arrayは集約の初期化で初期化する必要があります。だから実際にこれ:

std::array<int, 3> a {1, 2, 3};

これは構文上の砂糖です:

std::array<int, 3> a {{1, 2, 3}};

ご覧のとおり、最初の例では配列を値で初期化しているように見えますが、実際にはブレースされたinit-listを使用した集計の初期化です。これは、2番目の状況では1日として明確です。だから、それは初心者向けです。

では、なぜこれが機能しないのでしょうか?

std::array<std::pair<int, int>, 3> b {{1, 11}, {2, 22}, {3, 33}};

簡単に言えば、コンパイラは配列の初期化に使用している構文のタイプを区別できません。 {1, 11}は、初期化リストとして解釈され、最初のバージョンを使用するか、ペアとして解釈されて2番目のバージョンに進むことができます。

このコード:

std::array<std::pair<int, int>, 3> b {{{1, 11}, {2, 22}, {3, 33}}};.

あいまいさを取り除きます。

ソース: http://en.cppreference.com/w/cpp/language/aggregate_initialization

0
bartop