22

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 对上述示例的行为(即给出错误)是正确的,否则初始化的(){}形式之间会出现奇怪的不一致。

编辑:按照Kerrek SB的建议,我尝试delete了复制构造函数foo

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

行为保持不变。

4

1 回答 1

11

对于列表初始化,如果列表的元素有一个元素(此处为w),并且考虑了具有参数“对 const/volatile X 的引用”的类的构造X函数,则不考虑用户定义的转换。所以不能同时使用的复制和移动构造函数foo。所以foo(int)构造函数是明确选择的。

所以 Clang 在这里是正确的。

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

于 2013-04-17T09:19:10.250 回答