c++ - 何时在函数外部返回值使用 move 与复制?

标签 c++ c++11 copy return move

看完这篇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):

  • f 使用 move
  • g 使用 move
  • h 使用拷贝

据我所知,用于决定是使用复制还是 move 的规则在 12.8.32 中有描述:

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

其中引用了12.8.31的规则:(我只展示了相关部分)

  • 在具有类返回类型的函数的 return 语句中,当表达式是具有相同类型的非 volatile 自动对象的名称(函数或 catch 子句参数除外)时cvunqualified 类型作为函数返回类型,通过构造自动对象直接进入函数返回值可以省略复制/move 操作
  • 何时复制/move 未绑定(bind)到引用 (12.2) 的临时类对象 对于具有相同 cv-unqualified 类型的类对象,可以省略复制/move 操作 将临时对象直接构建到省略复制/move 的目标中

遵循这些规则,我了解 f 和 h 会发生什么:

  • f 中的拷贝符合省略条件,因此被 move 。 (参见粗体部分)
  • h 中的拷贝不符合省略条件,因此被复制。

g 呢?

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

最佳答案

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

A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. The presence of parentheses does not affect whether the expression is an lvalue. The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.

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


在即将发布的标准 C++14 中,这一点已得到澄清 §12.8p32(强调我的):

When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an 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
}

关于c++ - 何时在函数外部返回值使用 move 与复制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23914289/

相关文章:

c++ - 引用 front 和 pop_front

c++ - 如何使用非默认构造函数实例化模板类

HTML 5 : Canvas copying Pixel Data within a Polygon

c++ - 如何获取新的数组值? C++

c++ - 如果你有一个固定大小的数组,你需要遍历它 !n 次,使用二分查找如何改变时间复杂度?

c++ - Totalview:有没有办法硬编码断点?

c++ - 对模板替换示例感到困惑

c++ - C++中原子变量的线程安全初始化

linux - 用于复制 (scp) 和重命名的 Shell 脚本

java - IDeepCopy接口(interface)java