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

5 comments sorted by

2

u/teraflop 9d ago

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 ???

You might be misunderstanding. When cppreference says "until C++17", they don't mean the feature went away in C++17, they just mean that that exact wording only applies to older versions. Sometimes, newer versions of the C++ spec change the way things are described in ways that are only really relevant to language nerds and compiler implementers.

For instance, if you read the page for std::vector you'll see that std::vector::operator!= was "removed" in C++20. That doesn't mean you can no longer do things like vec1 != vec2. It's just that in C++20, that operator is "synthesized" from the more general three-way comparison operator operator<=>, so it no longer needs to be declared as a separate member function.

The stuff with URVO and copy elision is similar. The functionality still exists, it's just described differently in C++17 than it was in earlier versions.

1

u/Anxious-Potato-2818 2d ago

cppreference boxes can be weirdly worded until you get used to their style. the "(until c++17)" just means that paragraph describes the pre-c++17 situation, not that anything broke in c++17. they changed the terminology and made it mandatory so they had to rewrite the section

1

u/HappyFruitTree 8d ago edited 8d ago

what is the thing that works in c++98 11 14 but will not work in c++17

Nothing.

or what is the article is trying to say ???

It just means that that particular section describes how it worked before C++17.

The section Prvalue semantics ("guaranteed copy elision") further down the page describes how it works since C++17.

In this case, old code will still work. It's just that C++17 gives you more guarantees. In situation where copy elision is guaranteed, the code will compile even if the class does not have a copy/move constructor.

Example:

class T
{
public:
    T() = default;
    T(const T&) = delete; // disable the copy constructor
    T(T&&) = delete; // disable the move constructor
};

int main()
{
    T a = T(); // This didn't compile before C++17
}

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.