Why doesn't clang optimize this with NRVO?

I am trying to understand why a reasonably good C ++ 11 compiler (clang) does not optimize this code and wonders if anyone has opinions.

#include <iostream> #define SLOW struct A { A() {} ~A() { std::cout << "A d'tor\n"; } A(const A&) { std::cout << "A copy\n"; } A(A&&) { std::cout << "A move\n"; } A &operator =(A) { std::cout << "A copy assignment\n"; return *this; } }; struct B { // Using move on a sink. // Nice talk at Going Native 2013 by Sean Parent. B(A foo) : a_(std::move(foo)) {} A a_; }; A MakeA() { return A(); } B MakeB() { // The key bits are in here #ifdef SLOW A a(MakeA()); return B(a); #else return B(MakeA()); #endif } int main() { std::cout << "Hello World!\n"; B obj = MakeB(); std::cout << &obj << "\n"; return 0; } 

If I run it with #define SLOW commented out and optimized with -s , I get

 Hello World! A move A d'tor 0x7fff5fbff9f0 A d'tor 

which is expected.

If I run this with #define SLOW enabled and optimized with -s , I get:

 Hello World! A copy A move A d'tor A d'tor 0x7fff5fbff9e8 A d'tor 

Which is clearly not so good. So the question is:

Why can't I see the NRVO optimization used in the case of "SLOW"? I know that the compiler should not use NRVO, but this would seem to be such a simple simple case.

In general, I try to encourage the SLOW style code because it is much easier for me to debug it.

+7
c ++ optimization c ++ 11 nrvo
source share
2 answers

The simple answer: because in this case it is not allowed to use a copy. The compiler is allowed only in very few and specific cases for copying. A quote from the standard is 12.8 [class.copy], paragraph 31:

... This exclusion of copy / move operations, called copying, is permitted in the following cases (which can be combined to eliminate multiple copies):

  • in a return statement in a function with the type of the returned class, when the expression is the name of a non-volatile automatic object (different from the function parameter or the catch-clause parameter) with the same non-automatic type cv as the return function, the copy / move operation can be omitted by building automatic object directly to function return value
  • [...]

Obviously, the type B(a) not A , i.e. copy exclusion is not allowed. Other cartridges in the same paragraph relate to things like throw expressions that issue copies of a temporary declaration and an exception. None of them apply.

+13
source share

The copy you see on the slow path is not caused by the lack of RVO, but by the fact that in B (MakeA ()), "MakeA ()" is the value of r, but in B (a) "a" is the value of l.

To make this clear, change the slow path to indicate where MakeA () is executed:

 #ifdef SLOW A a(MakeA()); std::cout << "---- after call \n"; return B(a); #else 

Output:

 Hello World! ---- after call A copy A move A d'tor A d'tor 0x7fff5a831b28 A d'tor 

Which shows that in

 A a(MakeA()); 

So the RVO really happened.

The fix that deletes the entire copy is as follows:

 return B(std::move(a)); 
+2
source share

All Articles