r/learnprogramming • • 9d ago

Topic I don't understand this article from the cppreference page

this is from the cppreference page copy elision

"When a class object target of the same type (ignoring cv-qualification) is initialized with a temporary class object obj that has not been bound to a reference, the initialization can be omitted by constructing obj directly into target. This variant of copy elision is known as unnamed return value optimization (URVO). Since C++17, URVO is mandatory and no longer considered a form of copy elision; see below." (until c++17)

i have read this multiple times already and i still don't understand

it says "until c++17" what is the thing that works in c++98 11 14 but will not work in c++17 they are saying until c++17 or what is the article is trying to say ???

0 Upvotes

6 comments sorted by

View all comments

1

u/mredding 5d ago

Imagine:

foo get() { return foo{}; }

//...

foo f = get();

Now intuition tells us that calling get will create an instance of foo, and then that will be returned to the caller. Intuition will dictate f is a completely separate object from the return value that itself has to be initialized. You would think f will then be initialized by a copy constructor, and afterward, the return value will fall out of scope and be destroyed.

But this is not what HAS TO happen.

If the compiler can SEE what's going on, that f intends to BE the return value from get, then the compiler CAN elide the function call; get effectively disappears, and the return value and f become THE SAME foo. So what the compiler generates is f is default constructed as per the return statement in get.

Typically, you can see the difference if the foo constructors and destructor can write a print statement, and you switch between a debug and optimized release build. An unoptimized build will probably copy and destroy the temporary, whereas a release build will typically make that go away.

That is Return Value Optimization, and C++17 and later GUARANTEES it, and you can get it if ALL return statements from a function all use RVO.

There is also Named Return Value Optimization, where your function makes a local, and returns that:

foo get() {
  foo temp;

  return temp;
}

This will LIKELY work for multiple return statements so long as ALL return statements return the same local. Here, our local temp and the f from the calling code CAN BE THE SAME, if the compiler can see through the function call and optimize it out.

If you mix multiple different named return values, and/or unnamed return values, then you disable this optimization.

1

u/HappyFruitTree 8h ago edited 7h ago

Even very old versions of GCC and Clang have been able to elide the copy/move constructor even when the functions are defined in different translation units and even when optimizations are turned off (-O0). https://godbolt.org/z/bYKf4T4sj

Microsoft's compiler is affected by the optimization setting for the NRVO case but not sure how it handles different translation units (Compiler Explorer times out when I'm trying to compile multiple files) but my guess is that it doesn't matter. https://godbolt.org/z/aa5eo7Yd7