2

How can I trust the compiler for non-guaranteed copy elision (in case of return value optimization) in this example code:

struct X { X() : size(10000), very_large_buffer(new char[size]) { } X(const X& src) : size(src.size), very_large_buffer(new char[src.size]) { memcpy(very_large_buffer, src.very_large_buffer, size); } X(X&& src) : size(src.size), very_large_buffer(src.very_large_buffer) { } char* very_large_buffer = nullptr; size_t size = 0; }; X foo() { X x; // some code return x; } X x = foo(); 

I checked with g++ 11.4 and it does not call copy or move constructor, which means copy elision happens. If I add std::move in the return statement above:

return std::move(x); 

Then move constructor is called. However without std::move, in case the compiler does not decide to do copy elision, the copy constructor will be called, which I would like to avoid.

So how can I both: use compiler's not guaranteed copy elision (i.e. not to use std::move) and be sure that elision will happen and copy constructor be called?

I mean: in my example I added debug prints and saw that compiler does the elision, but I cannot do this for all my other code. So I would better have copy elision, but if this is not guaranteed, I would much move constructor called, so I need std::move. But if I use std::move copy elision will neve happen. So what is the use of non-guaranteed copy elision then?

11
  • 1
    "the copy constructor will be called, which I would like to avoid." - It won't. The NRVO spec has two parts: the better known and optional part is complete elision of extra objects, but the lesser known and mandatory part is that local movable entities will be moved implicitly in certain contexts (like a return statement). Commented May 16, 2024 at 9:57
  • What are the compiler options you used? Commented May 16, 2024 at 9:57
  • 1
    You don't need std::move in return statements. See stackoverflow.com/questions/14856344/… Commented May 16, 2024 at 9:58
  • 1
    In manner to know how many object been used add a destructor. your code been optimized to use a single instance of X. so no other contractor been created, only the default. Commented May 16, 2024 at 10:04
  • 1
    I'm late to the party and just found gcc14 has -Wnrvo. ref link This should be what you really want. Commented May 17, 2024 at 5:09

1 Answer 1

-1
  1. "upgrade" to a higher C++ standard (at least C++ 17)
  2. make your objects non-copyable

The first one will guarantee the optimization, while the second one will ensure that you get an error when it doesn't happen. And catch all the other instances where you're accidentally doing a copy.

Sign up to request clarification or add additional context in comments.

4 Comments

NRVO is not guaranteed, even in C++17.
@interjay but when it does, it no longer needs a copy-constructor to exist (like C++11 did). So, if you have a non-copyable object, you're either getting the optimzed return or an error; in the 2nd case, you can work out what the problem is and fix it.
It doesn't need a copy constructor in C++11 either, just a move c'tor (which a copy c'tor can stand in for).
But sometimes I do want my object copied, so I do need copy constructor

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.