6

次のプログラムを検討してください。

struct X
{
    X(int, int) { }
    X(X&&) { }
};

int main()
{
    X x( {0, 1} ); // Doesn't compile on ICC 13.0.1, compiles on
                   // Clang 3.2, GCC 4.7.2, and GCC 4.8.0 beta.
}

GCC 4.7.2、GCC 4.8.0、およびClang 3.2でコンパイルすると、このプログラムは次のことを行います(*)。

  1. 次に、値をコンストラクターにX渡す型の一時を構築します。01
  2. 移動-Xその一時的なものから構築します。

代わりに、ICC 13.0.1では、コンパイルされません。

質問#1:誰が正しいですか?

(*)実際には、一時の作成とmoveコンストラクターの呼び出しは省略されていますが、-fno-elide-constructorsオプションを使用してコンパイルし、コンストラクターにいくつかの印刷出力を追加すると、これが行われていることがわかります。


ここで、上記のプログラムのわずかなバリエーションを考えてみましょう。ここでは、均一な初期化を使用して直接初期化していますx

int main()
{
    X x{ {0, 1} }; // ERROR! Doesn't compile.
//     ^........^
}

括弧の代わりに中括弧を使用してここで何かを変更することは期待していませんが、どういうわけかそうです。このプログラムは、テストしたコンパイラ(Clang 3.2、GCC 4.7.2、GCC 4.8)ではコンパイルされません。 0ベータ、およびICC 13.0.1)。

質問2:なぜですか?

4

1 に答える 1

2

これは、すべてのコンパイラのバグにすぎません。§8.5.4/3は言う、

タイプTのオブジェクトまたは参照のリスト初期化は次のように定義されます。

—初期化子リストに要素がなく、Tがデフォルトのコンストラクターを持つクラス型である場合、オブジェクトは値で初期化されます。

—それ以外の場合、Tがアグリゲートの場合、アグリゲートの初期化が実行されます(8.5.1)。

—それ以外の場合、Tがstd :: initializer_listの特殊化である場合、initializer_listオブジェクトは以下に説明するように構築され、オブジェクトの初期化に使用されます…</ p>

—それ以外の場合、Tがクラス型の場合、コンストラクターが考慮されます。該当するコンストラクターが列挙され、オーバーロード解決(13.3、13.3.1.7)によって最適なコンストラクターが選択されます。引数のいずれかを変換するためにナローイング変換(以下を参照)が必要な場合、プログラムの形式が正しくありません。

—それ以外の場合、Tが参照型の場合、Tによって参照される型の一時的なprvalueはリストで初期化され、参照はその一時的なものにバインドされます。

—それ以外の場合、初期化子リストに単一の要素がある場合、オブジェクトまたは参照はその要素から初期化されます。要素をTに変換するためにナローイング変換(以下を参照)が必要な場合、プログラムの形式が正しくありません。

…</p>

かなりの数のケースがあります。実際には、右辺値参照にバインドされたprvalue一時オブジェクトをリスト初期化していることに注意してください。

GCCに関しては、上記の最後の項目を単一要素の初期化子リストに適用しようとしていると思います。コンストラクターの署名をに変更するとX(X&&, int = 3)、初期化子{ {0, 1} }は失敗しますが{ {0, 1}, 3 }成功します。要素はbraced-init-listであるため、単一の項目は成功するはずです。この場合、parensと同様に、余分な中括弧を使用できると考えられます。しかし、この失敗は、ブレースの省略を伴うGCCの他の欠点と同様です。

私の高レベルの印象は、コンパイラがリストを型を持つオブジェクトとして処理しようとすると問題が発生するということですが、そうではありません。それを括弧で囲まれた引数リストのようなものに戻すのは難しいです。

エラーメッセージをより具体的に見ると(LWSリンクに感謝します)、

  • ICCは、表現を期待していると主張しています。基本的な文法によれば、braced-init-listsは、式だけでなく、他のbraced-init-listsを囲むことができるため、これは間違っています。

  • Clangは、「候補コンストラクターは実行可能ではありません。初期化子リストの引数を「X」に変換できません」と述べていますが、変換が明示的である場合は、を使用して機能しX x{ X{0, 0 } };ます。それは意味がありません。リストには変換元のタイプがないため、変換ではありません。それはリストの初期化です。

  • GCCは、「引数1の''から'X &&'への既知の変換はありません」と述べており、参照にバインドするための一時的なものを定義するまでには至っていないことを示唆しています。Clangと同様に、偽の変換を試みているようで、指定X{0,0}すると修正されます。

于 2013-02-28T03:00:13.610 に答える