Reputation: 51
I'm currently struggling with c++ and copy elision, specifically "named return value optimization" (NRVO), to be able to implement a factory-function pattern. I cannot obtain consistent behavior across different compilers. My mwe:
#include <iostream>
struct base {
virtual ~base() { std::cout << "dtor base\n"; }
};
struct derived : public base {
~derived() { std::cout << "dtor derived\n"; }
};
derived f() { return derived(); }
int main(int argc, char *argv[]) {
std::cout << "start\n";
new derived(f());
std::cout << "done. should have leaked!\n";
}
Note: Removing virtual base-dtor solves the problem, but I need it for my real implementation.
In case of gcc 5.4.0 the dtor is called, no copy elision is performed:
$ g++ test2.cpp && ./a.out
start
dtor derived
dtor base
done. should have leaked!
When using gcc 5.4.1 (Ubuntu calls it 5.4.1, I assume this is svn-head), all clangs I could get my hand on as well as various other gccs perform elision and successfully leak the memory:
$ g++ test2.cpp && ./a.out
start
done. should have leaked!
As I read the different places across the internetz, the compiler is allowed to do copy elision but not required. Only c++17 introduces guaranteed copy elision. So is this a bug in gcc 5.4.0, or is it just implementing the standard differently?
Upvotes: 2
Views: 1187
Reputation: 238441
In case of gcc 5.4.0 the dtor is called, no copy elision is performed:
start dtor derived dtor base done. should have leaked!
Actually, copy elision was performed. Otherwise you would have seen
start
dtor derived
dtor base
dtor derived
dtor base
done. should have leaked!
There are two opportunities for copy elision here. One is RVO (note thta it isn't NRVO, just regular unnamed RVO) in f
, and the other is copy construction of *derived
from temporary. GCC 5.4.0 performed one of the possible copy elisions.
all ... perform elision and successfully leak the memory
GCC 5.4.0 also succesfully leaked the memory pointed by derived
. The other compilers never created the temporary that 5.4.0 did and later destroyed.
As I read the different places across the internetz, the compiler is allowed to do copy elision but not required.
Correct.
So is this a bug in gcc 5.4.0
No. A compiler that doesn't always implement copy elision is standard compliant. See the highlighted part of my previous quote from your question.
Upvotes: 3
Reputation: 62613
Copy elision is an optional optimization until C++17, and even in C++17 it is only mandatory in certain cases. As far as I remember, (N)RVO copy elision is not mandatory even in C++17 — the only mandatory copy-elision case is an initialization with temporary.
(N)RVO should never alter your program's behaviour and should never be required for your program to function properly. You should write your code in such a way that it works properly regardless of (N)RVO, and simply works faster when/if (N)RVO kicks in.
Upvotes: 5