Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chapter 3 - Copy elision #5

Open
algor opened this issue Jun 26, 2024 · 0 comments
Open

Chapter 3 - Copy elision #5

algor opened this issue Jun 26, 2024 · 0 comments

Comments

@algor
Copy link

algor commented Jun 26, 2024

I think the section can be improved.

It starts with presenting a code that

  1. Creates and returns (by value) a named variable of type Product from createProduct function.
  2. calls the above function to instantiate a variable named created of type Product within
    the main function.

The text then shows an output from the code and says that it contradicts the previous statement that the
return should result in a call to a copy constructor.

The text then explains NRVO as

The compiler can see through the initialization, deduce that the temp object
is used only to initialize created, and can “compress” the creation steps.

And then shows the output with no-elide-constructors flag applied revealing a copy-construction.

It then tries to explain a mandatory optimization applied since C++17 by saying

Not going into details, it will elide additional copies when there’s an unnamed
temporary object from which we initialize a new entity

then presenting a modified variant of the initial code (which uses a braced list initialization
for the function return statement), giving a link to the Compiler Explorer session with the flag
mentioned above not applied and saying that with this code we will always see the output
only with Product(int, const std::string&) constructor output present.

Then it tries to explain the effect saying

In other words, the temporary from createProduct is skipped and used to initialize the
created object directly.

And concludes with referring to another book of the same author for details.

In my opinion the order of presenting the material, the choice of compiler version in combination
with the no-elide-constructors flag applied are not helpful.

To me the major issue of the section is that the text does not explain right from the start
how many copy-constructions we should expect without any optimizations in place.
It only mentions the temporary object created as a result of the function invocation in its next-to-last
paragraph. It never explicitly says that the first copy constructor is expected to be called to create a temporary
to hold the result of the function return value and the second one to copy-construct variable
created within the main function.

And without explicitly saying this the reader is left unaware of what is actually being optimized in
different versions of the code.

In the first part of the section when we talk about NRVO we do not explain which of the two
copy-constructions are eliminated by NRVO and which of them we are undoing by applying
the no-elide-constructors flag. We are lumping these things together by talking about
compiler "compressing" the creation steps.
We mention created variable initialization in
the context of NRVO while the variable is supposed to be instantiated from a temporary
object that keeps the result of the function invocation. And this copy-initialization is optimized
by the mandatory optimization that happens even with the flag applied.
We should have explicitly said which of the two optimization we got rid of by applying the flag.
That by applying the flag we revealed the copy-construction of the temporary for the return
statement which was otherwise optimized out by NRVO.

When we present a new version of the code for createProduct function that uses case #8 from
copy-list-initialization initializing
the temporary thus manually (with the code change) removing the first case of copy-initialization
we would otherwise have in the original code we never explain to the reader that we are doing the work
that the optional NRVO did for us previously.

Apparently the new version of the code assumes that some temporary object is created which is
then optimized out by the mandatory optimization we've just described.
But why did we change the code? A temporary that is mandatory-optimized was already present
in the previous version of the code.

And what are we trying to show with the last output in comparison with the previous one?
We already did not see the copy-construction output without the flag applied.
And the Compiler Explorer session linked from the last code example doesn't have the flag applied.
So we applied the flag in the previous example, then changed the code for some unexplained reason,
with unexplained to the reader consequences, removed the flag, got the same output as initially.
What should reader learn from this paragraph or the entire section?

My suggestion for the order / content presented in this section is:

  1. Present the initial code linked to Compiler Explorer session compiled under C++14 with no-elide-constructors applied.
  2. Explain that we would ordinarily have two copy-constructions in our code a) for constructing a temporary object that holds the returned value of the function b) for copy-instantiating the created variable.
  3. Explain the effects of mandatory copy-elision applied since C++17. Tell reader which of the two copy constructions explained in the previous step this optimization eliminates by linking to the session of Compiler Explorer which uses C++17 standard but keeps the no-elide-constructors flag applied.
  4. Explain the effects of NRVO and, again, for which of the two copy-construction it works by now removing the flag and using the same C++17 standard for the linked session of Compiler Explorer.
  5. (Optionally) If we really want to present the case of mandatory copy elision in isolation (using the second variant of the code with braced copy-list initialization in return statement then we should definitely explain readers what we are doing and why.

It would also be helpful to refer user to the output of cppinsights.io code using C++14 standard and showing the
/* NRVO variable */ comment within createProduct function and the second of the two copy/constructions
presented in the cppinsights output as Product created = Product(createProduct());

Hope this helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant