テンプレート変換演算子と暗黙のコピーコンストラクタに関する曖昧さ 質問する

テンプレート変換演算子と暗黙のコピーコンストラクタに関する曖昧さ 質問する

次のコードでは、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 を参照してください。

おすすめ記事