10

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)在这种情况下,返回大容器的方法是什么?

谢谢

4

2 回答 2

5

Yes, the compiler can perform RVO. I cooked up some testing code and ran it through 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 produces

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

Conspicuously absent from the disassembly is any call to the copy or move constructor of M.


Also, the paragraph of the standard setting out the criteria for copy elision draws no distinction between virtual and nonvirtual member functions, and whenever the standard for copy elision is met, overload resolution for the return statement "is first performed as if the object were designated by an rvalue".

That is to say, in a function

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

If copy elision can't take place for whatever reason, and a move constructor is available, return m; will always invoke the move constructor rather than the copy constructor. Hence, there's no need to use std::move for the return statement if you are returning a local variable.

于 2014-10-09T17:31:00.867 回答
1

如果你return std::move(result);,你什么都得不到,你可能会失去。所以不要这样做。

您无法获得任何东西,因为标准明确规定“如果满足 RVO 条件,或者您正在返回一个参数,请先尝试以rvalue的形式返回,并且只有当它无法编译时,才以lvalue 的形式返回。所以即使你return result;,编译器被迫先尝试return std::move(result);

您可能会输,因为return std::move(result);如果 RVO 适用,则专门防止 RVO。

于 2014-10-09T13:31:39.843 回答