虚函数可以成为RVO的候选者(返回值优化)吗?

时间:2014-10-09 13:22:28

标签: c++ virtual move rvo

C ++编译器是否能够将RVO应用于虚拟功能?

在这种情况下:

class AbstractReader
{
//...
public:
    virtual std::vector<float> getFloatVector() = 0;
//...
}

class XmlReader : public AbstractReader
{
//...
public:
    virtual std::vector<float> getFloatVector()
    {
        std::vector<float> result;

        //Do some parsing here...

        return result;
    }
//...
}



class BinaryReader : public AbstractReader
{
//...
public:
    virtual std::vector<float> getFloatVector()
    {
        std::vector<float> result;

        //Do some decoding here...

        return result;
    }
//...
}

RVO可以适用于return result;行吗?我猜不会。

然后,在这种情况下,std::move(result)是否有办法返回大型容器?

由于

2 个答案:

答案 0 :(得分:4)

是的,编译器可以执行RVO。我编写了一些测试代码并通过godbolt运行:

struct M {
  M();
  M(const M&);
  M(M &&);
  ~M();
  double * ptr;
};

M getM();

struct A {
  virtual M foo() = 0;
};

struct B : A {
  virtual M foo() override;
};

M B::foo(){
  M m;
  return m;
}

struct C : B {
  virtual M foo() override;
};
M C::foo(){
  M m = getM();
  return m;
}

A* getA();

int main(){
  A* p = getA();
  M m = p->foo();
}

g++ -O3生成

B::foo():
    pushq   %rbx
    movq    %rdi, %rbx
    call    M::M()
    movq    %rbx, %rax
    popq    %rbx
    ret
C::foo():
    pushq   %rbx
    movq    %rdi, %rbx
    call    getM()
    movq    %rbx, %rax
    popq    %rbx
    ret
main:
    subq    $24, %rsp
    call    getA()
    movq    (%rax), %rdx
    movq    %rax, %rsi
    movq    %rsp, %rdi
    call    *(%rdx)
    movq    %rsp, %rdi
    call    M::~M()
    xorl    %eax, %eax
    addq    $24, %rsp
    ret

反汇编中明显缺席的是对M的副本或移动构造函数的任何调用。


此外,标准中设置复制省略标准的段落没有区分虚拟和非虚拟成员函数,只要满足复制省略的标准,return语句的重载解析&#34 ;首先执行,好像对象是由右值&#34;指定。

也就是说,在一个函数中

M foo() {
    M m = /*...*/;
    return m;
}

如果由于某种原因无法进行复制省略,并且移动构造函数可用,return m;将始终调用移动构造函数而不是复制构造函数。因此,如果要返回局部变量,则不需要使用std::move作为return语句。

答案 1 :(得分:0)

如果你return std::move(result);,你无法获得任何收益,而你可能会失败。所以不要这样做。

你无法获得任何东西,因为标准明确说明“如果满足RVO条件,或者你正在返回一个参数,首先尝试返回 rvalue ,只有当它不能编译时,返回as lvalue。所以,即使你return result;,编译器强制首先尝试return std::move(result);

您可能会输,因为return std::move(result);会特别阻止RVO(如果适用的话)。