【问题标题】:Why is r-value reference constructor called in this case?为什么在这种情况下调用 r 值引用构造函数?
【发布时间】:2016-12-20 08:29:00
【问题描述】:
#include<iostream>
using namespace std;

struct B{};

struct A
{
    A(const B &)
    {
        cout<<"A(const B &)"<<endl;
    }
    A(B &&)
    {
        cout<<"A(B &&)"<<endl;
    }
};

A get()
{
    B b;
    return b;
}

int main()
{
    get();
}

我用VC++14.2和GCC 5.4.0测试了代码,都输出了:

A(B &&)

为什么没有输出

A(const B &)

?

这段代码和copy elision有关系吗? (但是A和B是不同的类型,所以copy elision在这里应该不行)

【问题讨论】:

  • 这其实是一个很有趣的问题。我发现 12.8/32 中的措辞有些不清楚:返回表达式的类型是否必须与函数的返回类型相同才能应用规则,这一点尚不清楚。我认为您可以将其视为 any 返回的具有自动存储的对象被视为由右值指定。不同的编译器think differently about this.
  • CWG1579 似乎与此相关。
  • @KerrekSB 根据 (31.1),when the expression is the name of a non-volatile automatic object (other than a function parameter or a variable introduced by the exception-declaration of a handler (15.3)) with the same type (ignoring cv-qualification) as the function return type,我认为 same type 必须与 return type 完全相同。这意味着如果我返回 A 对象,copy elision 将起作用。但是从BA 的隐式转换发生在copy elision 之前,copy elision 不应影响implicit conversion
  • 这不是问题,但不要使用std::endl,除非您需要它的额外功能。 '\n' 结束一行。

标签: c++ return implicit-conversion


【解决方案1】:

根据 C++14 发布前的审查,return-as-rvalue 规则发生了变化。更改是在过程后期添加的,并由CWG Issue 1579 捕获,它用以下措辞修改了 12.8/32:

或者当return 语句中的表达式是一个(可能用括号括起来的)id-expression,它命名一个在主体中声明的具有自动存储持续时间的对象

这意味着返回任何局部变量现在将由该变量指定的对象首先视为右值(如果重载决议失败,则重试)。

由于 CWG 问题被认为是语言中的一个缺陷,因此即使在“C++11 模式”下,编译器也可以实现这一新规则。缺陷的重点是“它总是以这种方式工作”,所以严格来说这不是 C++11 和 C++14 之间的变化,而是 C++11 的含义被修改了2014 年。

【讨论】:

  • 声明可以在 n4594 §12.8 32 中找到。我认为只有在遇到copy elision 时才有效!!!
  • @Caesar:是的,过去就是这样。但是链接的核心问题是“如果它也能更普遍地工作会很好”,然后有一个国家机构评论改变了它,所以它被改变了。链接的问题包含一个与您的非常相似的激励示例。
  • @Caesar 是的,我每次阅读时都会犯这个错误。这是一个非常复杂的句子。但是这样想。如果您按名称返回一个自动存储变量,那么它肯定是安全的,那么为什么不让语言自动为您做呢?
  • @Barry 实际上,我不喜欢“太聪明”的编译器。哈哈
  • @Caesar:对不起,我错了,你说的是对的——如果你明确删除,那么程序确实是错误的。所以实际上 Clang 是错误的,这是一个已知的错误。只有当 隐式定义的 移动构造函数被选中并被删除时,重载解析才会失败并作为左值再次尝试。
【解决方案2】:

复制省略与 A 有关,不是在 get() 堆栈帧中构造的。 发生的情况是返回的b(这是暂时的,因为“因为返回而死”:这是从 C++14 开始),用于在主堆栈帧中构造复制省略的A

而且由于临时对象绑定了右值引用,这就是您观察到的。

【讨论】:

  • 在哪里可以找到 ISO C++ 标准中的声明(现在,n4594)?
  • 好吧,但是在执行copy elision之前有一个隐式转换。
  • 重点是b,因为return,被认为是临时的,因此在转换中绑定为&&。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-06
  • 1970-01-01
  • 2022-10-15
  • 1970-01-01
相关资源
最近更新 更多