Are C++ compilers able to apply RVO for virtual functions?
In this case:
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;
}
//...
}
Can RVO apply to return result;
lines? I would guess not.
Then, is std::move(result)
the way to go for returning large containers in that case?
Thanks
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.