【问题标题】:Ambiguity involving templated conversion operator and implicit copy constructor涉及模板化转换运算符和隐式复制构造函数的歧义
【发布时间】:2013-04-17 07:35:33
【问题描述】:

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的建议,我尝试deleteing foo的复制构造函数:

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

行为保持不变。

【问题讨论】:

  • 我会在这个上使用 GCC:转换为 intFoo 都是可能的,所以这看起来真的很模棱两可。不过,请尝试删除或默认复制/移动构造函数,然后进行比较。
  • @KerrekSB 删除的函数确实参与了重载决议。
  • @rhalbersma:好点子!
  • 显然(根据this talk 的幻灯片 17),大括号初始化样式不能用于 C++11 中的复制,这将使 Clang 正确。它被认为是一个缺陷,可能会在 C++14 中修复,使 GCC 正确。但是,我似乎在 C++11 中找不到阻止它的措辞,所以我无法给出明确的答案。
  • @MikeSeymour: litb,当然 :p 顺便感谢 Bjarne 的演讲,我没看过!

标签: c++ templates c++11 overload-resolution conversion-operator


【解决方案1】:

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

所以 Clang 在这里是正确的。

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

【讨论】:

    猜你喜欢
    • 2020-06-12
    • 2019-01-29
    • 2018-04-07
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多