2

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?

SergeyA
  • 61,605
  • 5
  • 78
  • 137

2 Answers2

5

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.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • As I read [this](http://stackoverflow.com/a/12953129/7374642), copy elision _can_ alter program behavior: It's the only form of optimization that elides (ha!) the as-if rule - copy elision can be applied even if copying/moving the object has side-effects. – fdgsydfgsdfgsdfg Jan 04 '17 at 15:16
  • 3
    @fdgsydfgsdfgsdfg To clarify, (N)RVO can alter the program's behaviour if the program depends on the creation of the temporaries and the side-effects. The point of SergeyA is that your program should never depend on such side-effects and if it doesn't, (N)RVO will not alter the behaviour. – eerorika Jan 04 '17 at 15:17
  • Yes, relying on copy elision was a bad idea... I got this ;-) – fdgsydfgsdfgsdfg Jan 04 '17 at 15:21
3

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.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326