【问题标题】:Move constructor suppressed by comma operator移动由逗号运算符抑制的构造函数
【发布时间】:2012-08-30 13:37:56
【问题描述】:

这个程序:

#include <iostream>
struct T {
    T() {}
    T(const T &) { std::cout << "copy constructor "; }
    T(T &&) { std::cout << "move constructor "; }
};
int main() {
    ([](T t) -> T { return t; })({}); std::cout << '\n';
    ([](T t) -> T { return void(), t; })({}); std::cout << '\n';
    ([](T t) -> T { return void(), std::move(t); })({}); std::cout << '\n';
}

当由 gcc-4.7.1 编译时输出 (link):

move constructor 
copy constructor 
move constructor 

为什么逗号操作符会有这种效果?标准说:

5.18 逗号运算符 [expr.comma]

1 - [...] 类型 结果的和值是右操作数的类型和值;结果与其右操作数[...]具有相同的值类别。如果右操作数的值是临时的,则结果是临时的。

我是否遗漏了一些允许逗号运算符影响程序语义的东西,或者这是 gcc 中的错误?

【问题讨论】:

  • 认为是命名对象通常不能被移动(见那里的#2),但是return t; 有一个明显的漏洞,它可以破坏命名的-对象规则并移动(参见#1)。但我不确定。如果有人确定,请发布答案。
  • @Matthew 谢谢!它已解决“扩展”问题,因此委员会愿意对其进行更改,但不认为当前的行为是错误。
  • 对。看起来 CWG 认为这不是一个错误并将其踢给了 EWG,最近在没有太多动静的情况下进行了讨论(基本上,我们同意它是 NAD,但我们愿意进行改进)。我不确定谁(如果有的话)正在积极追求这一点,但欢迎您通过标准提案摇摆并四处询问。

标签: c++ c++11 return move-semantics comma-operator


【解决方案1】:

自动移动基于复制省略的资格:

§12.8 [class.copy] p32

当满足或将满足省略复制操作的条件时,除了源对象是函数参数的事实,并且要复制的对象由左值指定时,重载决策选择构造函数首先执行复制,就好像对象由右值指定一样。 [...]

当返回表达式是自动对象的名称时,允许复制省略。

§12.8 [class.copy] p31

在具有类返回类型的函数中的 return 语句中,当表达式是非易失性自动对象的名称时(函数或 catch 子句参数除外)与函数返回类型相同的 cv 非限定类型,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作

插入逗号运算符后,表达式不再是自动对象的名称,而只是对一个对象的引用,从而抑制了复制省略。

【讨论】:

    【解决方案2】:

    t 一个局部的命名变量,因此是一个左值。逗号操作符的行为与记录一致。

    相反,您应该问为什么 return t; 允许 t 绑定到右值引用 - 才是真正的魔力。

    【讨论】:

      猜你喜欢
      • 2016-05-19
      • 2017-05-15
      • 2019-09-29
      • 1970-01-01
      • 2017-01-16
      • 2020-09-06
      • 1970-01-01
      • 2013-03-16
      • 1970-01-01
      相关资源
      最近更新 更多