48

演算子の右辺でイニシャライザ リストを使用できない理由がわかりません。検討:

class foo { };

struct bar
{
    template<typename... T>
    bar(T const&...) { }
};

foo& operator<<(foo& f, bar const&) { return f; }

int main()
{
    foo baz;
    baz << {1, -2, "foo", 4, 5};

    return 0;
}

最新のClang(gccも)は不平を言います:

clang.cc:14:9: error: initializer list cannot be used on the right hand side of operator '<<'
    baz << {1, -2, "foo", 4, 5};
    ^  ~~~~~~~~~~~~~~~~~~~~

    ^  ~~~~~~~~~~~~~~~

C++ 標準がこれを禁止するのはなぜですか? または別の言い方をすれば、なぜこれは失敗するのではなく

baz << bar{1, -2, "foo", 4, 5};

?

4

1 に答える 1

62

実際、C++11 の最終バージョンでは、二項演算子の右側 (または左側) で初期化子リストを使用できません。

まず、initializer-list は、標準の §5 で定義されている式ではありません。二項演算子と同様に、関数の引数は一般に式である必要があり、§5 で定義されている式の文法には、ブレース初期化リスト (つまり、純粋な初期化子リスト) の構文は含まれていません。型名が後に続くことに注意してください。ただし、式などの波括弧初期化リストによってbar {2,5,"hello",7})。

純粋な初期化子リストを便利に使用できるようにするために、標準ではさまざまな例外が定義されています。これらの例外は、次の (非規範的な) 注にまとめられています。

§8.5.4/1 [...] 注: リスト
初期化は、変数定義 (8.5)
の初期化子として、新しい式 (5.3.4) の初期化子として、
return ステートメント (6.6 ) で使用できます。 .3)
— 関数の引数として (5.2.2)
— 添え字として (5.2.1)
— コンストラクター呼び出しの引数として(8.5、5.2.3) —
非静的データ メンバーの初期化子として (9.2) )
— mem-initializer (12.6.2)
— 割り当ての右側 (5.17)
[...]

上記の 4 番目の項目では、純粋なイニシャライザ リストを関数の引数として明示的に許可し (これが機能する理由ですoperator<<(baz, {1, -2, "foo", 4, 5});)、5 番目の項目では添字式で許可し (つまり、 の引数としてoperator[]、たとえばmymap[{2,5,"hello"}]有効です)、最後の項目では右側でそれらを許可しています。代入側(ただし、一般的な二項演算子ではありません)。

, orのような二項演算子にはそのような例外はありません。したがって、それらの両側に純粋な初期化子リスト (つまり、型名が前に付いていないもの) を置くことはできません。+*<<

この理由については、Stroustrup と Dos Reis による 2007 年のドラフト/ディスカッション ペーパー N2215で、さまざまなコンテキストにおける初期化子リストに関する多くの問題について多くの洞察が得られます。具体的には、二項演算子に関するセクションがあります (セクション 6.2):

初期化子リストのより一般的な使用法を検討してください。例えば:

v = v+{3,4};
v = {6,7}+v;

演算子を関数のシンタックス シュガーと見なす場合、当然、上記の等価物は次のようになります。

v = operator+(v,{3,4});
v = operator+({6,7},v);

したがって、イニシャライザ リストの使用を式に拡張するのは自然なことです。演算子と組み合わされた初期化子リストが「自然な」表記法である多くの用途があります。
ただし、初期化子リストの任意の使用を許可する LR(1) 文法を作成することは簡単ではありません。ブロックも { で始まるため、初期化子リストを式の最初 (左端) のエンティティとして許可すると、文法が混乱します。
二項演算子の右側のオペランドとして、添え字、および文法の同様の分離された部分として初期化子リストを許可することは簡単です。本当の問題は、を許可;a={1,2}+b;せずに割り当てステートメントとして許可すること;{1,2}+b;です。イニシャライザリストを右辺として許可しても [原文のまま] ほとんどの演算子の左辺引数として許可するのは、あまりにも面倒なことであると思われます [...]

言い換えれば、イニシャライザリストは左側では有効にされていないため右側では有効にされておらず、左側では有効にされていません。 .

初期化子リスト構文の中括弧の代わりに別のシンボルを選択することで、問題を単純化できたのではないかと思います。

于 2012-07-12T06:10:06.090 に答える