次のコードでは、clang と gcc の動作が異なります。
struct foo
{
foo(int);
};
struct waldo
{
template <typename T>
operator T();
};
int main()
{
waldo w;
foo f{w};
}
このコードは、コンストラクタが呼び出された状態で clang によって受け入れられます。ただし、gcc は、コンストラクタと暗黙的に生成されたコピーおよび移動コンストラクタfoo(int)
との間の曖昧さについてエラーを出力します。foo(int)
test.cpp: In function 'int main()':
test.cpp:15:12: error: call of overloaded 'foo(<brace-enclosed initializer list>)' is ambiguous
foo f{w};
^
test.cpp:15:12: note: candidates are:
test.cpp:3:5: note: foo::foo(int)
foo(int);
^
test.cpp:1:8: note: constexpr foo::foo(const foo&)
struct foo
^
test.cpp:1:8: note: constexpr foo::foo(foo&&)
誰が正しいでしょうか?
また、foo f{w}
が に変更された場合 (中括弧から丸括弧への変更に注意)、gcc と clang の両方でエラーが発生することも興味深い点です。これにより、上記の例に対する gcc の動作 (つまり、エラーの発生) が正しいことを期待できます。そうでなければ、と の初期化形式foo f(w)
の間に奇妙な矛盾が生じます。()
{}
編集: 続くケレック SBの提案に従って、delete
のコピー コンストラクターを試しましたfoo
:
struct foo
{
foo(int);
foo(const foo&) = delete;
};
動作は同じままです。
ベストアンサー1
リストの初期化では、リストの要素に 1 つの要素 (ここではw
) があり、X
パラメータ「const/volatile X への参照」を持つクラスのコンストラクタが考慮される場合、ユーザー定義の変換は考慮されません。したがって、 のコピー コンストラクタと移動コンストラクタの両方はfoo
使用できません。したがって、foo(int)
コンストラクタは明確に選択されます。
つまり、Clang はここでは正しいのです。
編集: ここの標準担当者は、13.3.3.1p4 を参照してください。