移动由逗号运算符抑制的构造函数

Posted

技术标签:

【中文标题】移动由逗号运算符抑制的构造函数【英文标题】: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)。但我不确定。如果有人确定,请发布答案。 顺便说一句,这似乎是open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#2125 @Matthew 谢谢!它已解决“扩展”问题,因此委员会愿意对其进行更改,但不认为当前的行为是错误。 对。看起来 CWG 认为这不是一个错误并将其踢给了 EWG,最近在没有太多动静的情况下进行了讨论(基本上,我们同意它是 NAD,但我们愿意进行改进)。我不确定谁(如果有的话)正在积极追求这一点,但欢迎您通过标准提案摇摆并四处询问。 【参考方案1】:

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

§12.8 [class.copy] p32

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

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

§12.8 [class.copy] p31

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

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

【讨论】:

【参考方案2】:

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

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

【讨论】:

以上是关于移动由逗号运算符抑制的构造函数的主要内容,如果未能解决你的问题,请参考以下文章

知识总结7

c语言函数式宏逗号表达式

C 逗号运算符

为啥逗号运算符在运算符[]内部称为逗号运算符而不在运算符()内部调用?

变量赋值和逗号运算符

在 for 循环中声明的逗号运算符