C++ 重载解析在gcc和clang之间得到不同的结果

C++ 重载解析在gcc和clang之间得到不同的结果,c++,c++11,overloading,C++,C++11,Overloading,gcc 5.1.0给出了错误 /dev/fd/63:3:8:错误:调用重载的“B() 含糊不清 /dev/fd/63:3:8:注:候选人包括: /dev/fd/63:2:27:注:B::B(常数B&) /dev/fd/63:2:21:注:B::B(A) 而Clang3.6.0成功了 哪一个是对的?为什么? 对于gcc 5.1.0: 对于clang 3.6.0: 这可能类似于gcc和clang得到相同结果的情况 但这是一个不同的问题B(A)在这里是明确的。gcc和clang得到不同的结果。正确的

gcc 5.1.0给出了错误

/dev/fd/63:3:8:错误:调用重载的“B()
含糊不清
/dev/fd/63:3:8:注:候选人包括:
/dev/fd/63:2:27:注:B::B(常数B&)
/dev/fd/63:2:21:注:B::B(A)
而Clang3.6.0成功了

哪一个是对的?为什么?

对于gcc 5.1.0:

对于clang 3.6.0:

这可能类似于gcc和clang得到相同结果的情况


但这是一个不同的问题<代码>B(A)在这里是明确的。gcc和clang得到不同的结果。

正确的列表初始化语义是

struct A { A(int);};
struct B { explicit B(A); B(const B&);};
B b({0}); 
这很好。如果你写
bb({0}),gcc无法决定是直接调用
B(A)
,还是创建
B
{0}
),然后在第二阶段用
B(常量B&)
复制它。这两个选项之间没有优先级排序


这是语言问题,不是编译器的问题。见此。

差异可以减少到

B b{0};
在GCC上,根据标准(该标准规定,对于列表初始化,会考虑显式构造函数-因此它们可能会产生歧义-但不允许选择它们),这是失败的。Clang接受它并调用第二个函数

在你的情况下,@Columbo在回答问题时所说的话是适用的。不同的是,在你的例子中,
B(常数B&),因为
{0}->B
转换将面临两种可能性:显式构造函数或第二次递归使用复制构造函数。如上所述,clang不会考虑第一个选项,这次@Columbo的解释适用,复制构造函数不能再次使用,因为这需要用户定义的转换,因为我们只有一个元素(这里,
0
)。因此,在总结中,只有第一个构造函数成功并被接受


因为我知道这个问题是关于奇怪的重载解决规则,有些可能无法遵循,这里有一个更直观的解释。处于活动状态的规则依次为

  • b({0})
    表示转到,从那里开始是我们的第一个或第二个上下文。列举的两个构造函数是
    B(A){0}
    的code>和
    B(常数B&)

    • 对于
      B(A)
      而言,它使用单个用户定义的转换

    • 对于
      B(const B&)
      ,我们需要初始化一个
      const B&
      ,这使我们到达(通过“否则,如果T是引用类型,则T引用的类型的临时PR值是复制列表初始化…”的帮助)然后到达。结果或上下文具有候选
      B(A)
      B(常数B&)
      ,参数为
      0
      。这是我们的第二个OR上下文,是13.3.1.7中的复制列表初始化(根据over.ics.ref#2和dcl.init.list-3的要求)

      • 对于
        B(A)
        ,构造函数是显式的,因此被Clang忽略(与规范相矛盾),但被GCC接受(因此存在歧义)

      • 对于
        B(const B&)
        ,这是@Columbo处理的场景,因此禁止需要的用户定义转换。较新的草案不再有此规则(但可能会重新添加)。但是由于
        0
        const B&
        将是一个正常的用户定义转换(不是列表初始化),因此它无论如何都会忽略转换所需的显式构造函数(对于复制构造函数的这一潜在第二次使用),因此,用户定义的转换无论如何都是不可能的,而且这个规则的重要性比我在写上面的简短摘要时想象的要小得多


因此,对于GCC,它可以直接使用显式构造函数,此外还可以通过单一使用复制构造函数。对于clang,它只考虑直接使用显式构造函数,而不会像GCC那样通过使用复制构造函数的复制列表初始化来间接使用它。两个都不会考虑第二次使用复制构造函数,这里不相关。 B(a)< /C>是明确的,GCC和CLAN都得到相同的结果,但这里是不同的。我猜这是由于不同的默认标准/标准支持。@ CalbBo这里添加的扭曲是<代码>显式< /代码>,即使<代码> { 0 } > const b & < /COD>是复制列表初始化,但在过载解决过程中确实要考虑显式构造函数,如果选择了一个程序,只会使程序不正确。这可能足以使这个问题成为一个非杜撰。CLAN接受这一点的原因似乎是,在做133.1.7的过载解决方案时,不考虑显式构造函数,而不是考虑它们并在以后出错。看见这是可以理解的,因为理查德史密斯在年向委员会报告了混乱
struct A { explicit A(int); };
struct B { B(int); };
void f(A);
void f(B);

int main() {
    f({ 1 });
}