gcc和clang之间的重载分辨率差异涉及移动构造函数和'Derived(Base&&&)'构造函数

时间:2015-04-23 21:06:16

标签: c++ c++11 gcc clang overload-resolution

GCC(使用4.9测试)接受以下测试用例:

struct Base {};

struct Derived : Base {
    Derived();
    explicit Derived(const Derived&);
    explicit Derived(Derived&&);
    explicit Derived(const Base&);
    Derived(Base&&);
};

Derived foo() {
  Derived result;
  return result;
}

int main() {
  Derived result = foo();
}

Clang(使用3.5测试)拒绝它,并显示以下错误消息:

test.cpp:13:10: error: no matching constructor for initialization of 'Derived'
  return result;
         ^~~~~~
test.cpp:8:5: note: candidate constructor not viable: no known conversion from 'Derived' to 'Base &&' for 1st argument
    Derived(Base&&);
    ^
test.cpp:4:5: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
    Derived();
    ^

谁是对的?

1 个答案:

答案 0 :(得分:14)

我相信Clang在这里是正确的。 GCC不应该接受该代码。

原因是在return(强调我的)中指定[class.copy] p32语句中发生的对象副本的构造函数的重载解析方式:

  

当满足复制/移动构造函数的省略标准时,   [...],并且要复制的对象由左值,[...]指定,   重载决策首先选择复制的构造函数   表现为好像该对象是由右值指定的。如果是第一个   重载决议失败或未执行,或者类型为   所选构造函数的第一个参数不是右值   引用对象的类型(可能是cv-qualified),重载   再次执行分辨率,将对象视为左值。

在此示例中,符合elision的条件(通过[class.copy] p31中的第一个项目符号),并且要复制的对象由左值指定,因此本段适用。

首先尝试重载分辨率,就像对象是由右值指定的一样。 explicit构造函数不是候选者(请参阅下面的解释原因),因此选择了Derived(Base&&)构造函数。但是,这属于“所选构造函数的第一个参数的类型不是对象类型的右值引用”(相反,它是对象基类类型的右值引用),因此应该再次执行重载决策,将对象视为左值。

第二个重载决策失败,因为唯一可行的构造函数(同样,explicit构造函数不是候选者)有一个rvalue引用参数,它不能绑定到左值。 Clang显示导致的重载决策失败错误。

要完成解释,这就是为什么explicit构造函数不适合重载解析(所有重点都是我的)。

首先,[dcl.init] p15说:

  

在a的形式中发生的初始化    brace-or-equal-initializer condition (6.4),以及参数   传递,函数返回,抛出异常(15.1),处理一个   异常(15.3)和聚合成员初始化(8.5.1),是   称为复制初始化 。“

接下来,我们来看看[over.match.ctor] p1

  

对于复制初始化,候选函数所有转换   该类的构造函数(12.3.1)。

最后,我们看到explicit构造函数未在[class.conv.ctor] p1中转换构造函数:

  

构造函数声明而没有函数说明符   explicit 指定从其参数类型到其类类型的转换。这样的构造函数称为 转换   构造