13

这个程序:

#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 编译时输出(链接):

move constructor 
copy constructor 
move constructor 

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

5.18 逗号运算符 [expr.comma]

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

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

4

2 回答 2

14

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

§12.8 [class.copy] p32

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

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

§12.8 [class.copy] p31

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

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

于 2012-09-05T19:20:37.000 回答
8

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

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

于 2012-09-05T19:19:40.120 回答