阅读此question后。我创建了这个小小的测试:
class A{
public:
A(){}
A(const A&){printf("copy\n");}
A(A&&){printf("move\n");}
static A f(){
A a;
return a;}
static A g(){
A a;
return (a);}//could be return *&a; too.
static A h(){
A a;
return true?a:a;}
};
结果是(没有RVO和NRVO):
据我所知,12.8.32中描述了用于决定是否使用复制或移动的规则:
其中涉及12.8.31的规则:(我只显示相关部分)
按照这些规则,我理解f和h会发生什么:
g怎么样?
对我来说,它看起来很像h。我正在返回一个表达式,它不是自动对象的名称,因此我认为它会被复制但是它被移动了。这里发生了什么?
答案 0 :(得分:11)
在大多数情况下,写a
或(a)
没有区别。规范的相关部分是§5.1p6(强调我的):
带括号的表达式是一个主表达式,其类型和值与所附表达式的类型和值相同。括号的存在不会影响表达式是否为左值。除非另有说明,否则带括号的表达式可以与可以使用封闭表达式的上下文完全相同,并且含义相同。
因此,相同的推理适用于您为g
提供的函数f
的返回值。
在upcomming标准C ++ 14中,这已被阐明§12.8p32(强调我的):
当满足复制/移动操作的省略标准时,但不满足异常声明,并且要复制的对象由左值指定,或者当返回语句中的表达式为a(可能带括号)id-expression命名一个对象,该对象具有在body或statement-declaration-clause中最内层封闭函数或lambda-expression声明的自动存储持续时间,用于选择复制的构造函数的重载决策是第一个如同对象由右值指定一样。
对于那些想知道的人,当括号重要时,这是一个例子:
namespace N {
struct S { };
void f(S);
}
void g() {
N::S s;
f(s); // OK: calls N::f
(f)(s); // error: N::f not considered; parentheses
// prevent argument-dependent lookup
}
答案 1 :(得分:0)
请注意,如果您声明
const A a;
在您的示例中,它们都会复制。 该标准的第12.8节说“过载分辨率 首先执行选择复制的构造函数,就像对象是由rvalue指定的那样,“但如果a是const,那将是一个const rvalue,它与移动构造函数不匹配。