如何确保在不妨碍RVO的情况下移动?

时间:2019-04-29 08:52:20

标签: c++ c++11 language-lawyer rvo

从C ++ 11开始,从函数返回对象时,假设定义了移动构造函数和复制构造函数,则可能发生以下情况之一(另请参见本文结尾处的示例):

  1. qualifies for copy-elision,编译器执行RVO。
  2. 它有资格进行复制删除,并且编译器不执行RVO,但是...
  3. qualifies for the usage of move constructor并被移动。
  4. 以上均未使用并且使用了复制构造函数。

对于前3种情况,建议不要使用显式的std::move,因为无论如何都会执行移动,并且可能会阻止可能的RVO,请参见此SO-post

但是,在4.情况下,显式std::move将提高性能。但是,由于有些人既不懂标准又不懂汇编程序,所以需要花很多时间来区分情况1-3和4。

我的问题:有没有一种方法可以以统一的方式处理上述所有情况,例如:

  1. 不阻止RVO(情况1)
  2. 如果不执行RVO,则使用move-constructor(情况2、3和4)
  3. 如果没有move-constructor,则应将copy-constructor用作后备。

以下是一些示例,也可以用作测试用例。

所有示例均使用以下helper-class-definition:

struct A{
    int x;
    A(int x_);
    A(const A& a);
    A(A&& a);
    ~A();
};

1。示例::1.case,已执行RVO,live-demonstrationresulting assembler

A callee1(){
    A a(0);
    return a;
}

2。示例::1.case,已执行RVO,live-demonstrationresulting assembler

A callee2(bool which){
    return which? A(0) : A(1);
}

3。例如: 2.case,有资格进行复制删除,未执行RVO,live-demonstrationresulting assembler

A callee3(bool which){
    A a(0);
    A b(1);
    if(which)
      return a;
    else
      return b; 
}

4。例如:。3.大小写不符合复制删除条件(x是函数参数),但不符合复制条件,live-demonstrationresulting assembler

A callee4(A x){
    return x; 
}

5。例如: 4.case,没有复制删除或隐式移动(请参阅此SO-postlive-demonstrationresulting assembler

A callee5(bool which){
    A a(0);
    A b(1);
    return which ? a : b;
}

6。例如: 4.case,没有复制删除或隐式移动,live-demonstrationresulting assembler

A callee6(){
    std::pair<A,int> x{0,1};
    return x.first;
}

1 个答案:

答案 0 :(得分:3)

什么时候不能执行RVO?

如果满足以下任一条件,则编译器通常无法执行RVO:

  1. 哪个返回的局部变量取决于条件(局部变量在条件之前而不是在条件内部定义)
  2. 您要返回类,联合或结构的成员
  3. 您要取消引用指针以获取返回值(包括数组索引)

在情况1中,您应该使用std::move或使用if语句而不是三元运算符来编写代码。在情况2中,您必须使用std::move。在情况3中,您还应该显式使用std::move

确保移动结构的政策(如果可能)

处理情况1。我们可以依靠if语句(而不是三元运算符)返回局部变量来确保值被移动:

A whichOne(bool which) {
    A a(0); 
    A b(1); 
    if(which) {
        return a;
    } else { 
        return b; 
    }
}

如果您真的更喜欢使用三元运算符,请在两个条件上明确使用std::move

A whichOne(bool which) {
    A a(0); 
    A b(1); 
    return which ? std::move(a) : std::move(b); 
}

如果我只想移动a而不是b会怎样?我们可以通过在条件条件中显式构造它来处理这种情况。在这种情况下,由于三元数的两边都会产生 prvalue ,因此可以保证构造值接受RVO。

A whichOne(bool which) {
    A a(0);
    A b(1); 
    return which 
      ? A(std::move(a)) // prvalue move-constructed from a
      : A(b);           // prvalue copy-constructed from b
}

处理情况2和3。很简单:返回对象的成员,或者返回来自数组或指针的引用时,请使用std::move