何时返回函数外部的值使用move vs copy?

Arn*_*aud 8 c++ copy return move c++11

看完这个问题后.我创建了这个小小的测试:

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;}

 };
Run Code Online (Sandbox Code Playgroud)

结果是(没有RVO和NRVO):

  • f使用移动
  • g使用移动
  • h使用副本

据我所知,用于决定是否使用复制或移动的规则在12.8.32中描述:

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

这是指12.8.31的规则:(我只显示相关部分)

  • 在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cvunqualified类型的非易失性自动对象(函数或catch子句参数除外)的名称时,副本通过将自动对象直接构造到函数的返回值中,可以省略/ move操作
  • 当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-nonqualified类型的类对象时,可以通过将临时对象直接构造到目标中来省略复制/移动操作省略的复制/移动

按照这些规则,我理解f和h会发生什么:

  • f中的副本有资格获得elision,因此被移动.(参见粗体部分)
  • h中的副本不符合elision的条件,因此会被复制.

怎么样?

对我来说,它看起来很像h.我正在返回一个表达式,它不是自动对象的名称,因此我认为它会被复制但是它被移动了.这里发生了什么?

MWi*_*Wid 11

在大多数情况下,写作a或写作没有区别(a).规范的相关部分是§5.1p6(强调我的):

带括号的表达式是一个主表达式,其类型和值与所包含表达式的类型和值相同.括号的存在不会影响表达式是否为左值.除非另有说明,否则带括号的表达式可以在与可以使用所包含的表达式的上下文完全相同的上下文中使用,并且具有相同的含义.

因此,相同的推理适用于g您给出的函数的返回值f.


在升级标准C++ 14中,已经阐明了§12.8p32(强调我的):

当满足复制/移动操作的省略标准时,但不满足异常声明,并且要复制的对象由左值指定,或者当返回语句中的表达式是(可能带有括号的)id-时表达式,用于在最内层封闭函数或lambda-expression的body或parameter-declaration-clause中声明的具有自动存储持续时间的对象,首先执行重载决策以选择复制的构造函数,就像对象由rvalue指定一样.


对于那些想知道的人,当括号重要时,这是一个例子:

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
}
Run Code Online (Sandbox Code Playgroud)