涉及模板化转换操作符和隐式复制构造函数的模糊性

Ambiguity involving templated conversion operator and implicit copy constructor

本文关键字:复制 构造函数 模糊性 操作符 转换      更新时间:2023-10-16

clang和GCC在以下代码中的行为不同:

struct foo
{
    foo(int);
};
struct waldo
{
    template <typename T>
    operator T();
};
int main()
{
    waldo w;
    foo f{w};
}

clang接受此代码,并调用foo(int)构造函数。然而,gcc抱怨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}更改为foo f(w)(注意从大括号更改为括号),gcc和clang都会给出错误。这让我希望gcc在上面例子中的行为(即给出错误)是正确的,否则(){}初始化形式之间会出现奇怪的不一致。

EDIT:遵循Kerrek SB的建议,我尝试delete复制foo的构造函数:

struct foo
{
    foo(int);
    foo(const foo&) = delete;
};

行为保持不变

对于列表初始化,如果列表的元素有一个元素(这里是w),并且考虑带形参"reference to const/volatile X"的类X的构造函数,则不考虑用户定义的转换。因此,foo的复制和移动构造函数都不能使用。因此,毫无疑问地选择了foo(int)构造函数。

所以Clang是正确的。

编辑:对于这里的标准人员,参见13.3.3.1p4