【问题标题】:Overload resolution difference between gcc and clang involving move constructor and 'Derived(Base&&)' constructorgcc 和 clang 之间的重载解析差异涉及 move 构造函数和 'Derived(Base&&)' 构造函数
【发布时间】:2015-07-02 06:42:59
【问题描述】:

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();
    ^

谁是对的?

【问题讨论】:

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


    【解决方案1】:

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

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

    当满足删除复制/移动构造函数的条件时, [...],要复制的对象由左值指定,[...], 首先是为副本选择构造函数的重载决议 就像对象由右值指定一样执行。如果第一个 重载决议失败或未执行,或者如果 所选构造函数的第一个参数不是右值 引用对象的类型(可能是 cv 限定的),重载 再次执行解析,将对象视为左值。

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

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

    第二次重载决议失败,因为唯一可行的构造函数(同样,explicit 构造函数不是候选者)有一个右值引用参数,它不能绑定到左值。 Clang 显示了由此产生的重载解析失败错误。


    为了完成解释,这就是为什么explicit 构造函数不是任何重载解决方案的候选者(所有重点都是我的)。

    首先,[dcl.init] p15 说:

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

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

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

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

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

    【讨论】:

    • 但它说“如果第一个参数的类型不是右值引用,则再次执行”。因此,既然如此,就不会再次执行重载决议。
    • @Columbo:它说“如果第一个参数的类型不是右值引用对对象的类型”,则再次执行重载决议。在这里,它是一个右值引用,但不是对象的类型。 (注意这里的“对象”指的是段落开头的“要复制的对象”,是Derived类型的对象。)
    • @HighCommander4:您是否向 gcc 提交了错误报告?该程序仍然在编译最新版本的 gcc 时出现警告。见this如果你不介意我可以向gcc提交错误报告如果你还没有完成吗?
    猜你喜欢
    • 2014-03-13
    • 1970-01-01
    • 2013-12-08
    • 1970-01-01
    • 2019-02-01
    • 2013-07-07
    • 2015-02-24
    • 2012-08-14
    • 1970-01-01
    相关资源
    最近更新 更多