5

Nicol Bolas wrote the following in his answer in SO:

Copy elision was permitted to happen under a number of circumstances. However, even if it was permitted, the code still had to be able to work as if the copy were not elided. Namely, there had to be an accessible copy and/or move constructor.]

Why was it necessary (before the advent of "guaranteed copy elision") for the code to maintain a copy/move constructor even when copy-elision was permitted to happen?

Why does "guaranteed copy elision" free the programmer from these requirements?

Community
  • 1
  • 1
Wake up Brazil
  • 3,421
  • 12
  • 19

4 Answers4

4

When copy elision is/was not guaranteed (or required) by the standard, then there is no requirement for a compiler to implement it.

That meant the standard allowed compilers to support copy elision, but did not require them to. And, in practice, a number of compiler vendors chose to not implement copy elision. For those vendors it is a matter of cost - not implementing a feature consumes less developer effort. For programmers (the people who use compilers) it was a quality of implementation concern - a higher quality compiler was more likely to implement desirable optimisations, including copy elision, than a lower quality compiler - but also be more expensive to acquire.

Over time, as higher quality compilers become more freely available (by various definitions of "free" - not all are equivalent to zero cost), gradually the standard is able to mandate more features that were previously optional. But it didn't start that way.

With copy elision being optional, some compilers would rely on accessibility of relevant copy constructors, etc, and some would not. However, the notion of code which complies with requirements of the standard, which builds with one compliant compiler but not another, is naturally undesirable in a standard. Therefore the standard mandated a need for constructors to be accessible, even while permitting an implementation to elide them.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • Having code be accepted by some compilers but not others should be considered far less objectionable than having code which is accepted by all compilers but has different meanings. The set of tasks that could be accomplished by programs that might be rejected by some (or even most) implementations but would not invoke UB on any implementation that accepted them, would be much larger than the set of tasks that could be performed successfully on all implementations. IMHO, the Standard should define a category for such programs, and seek to maximize the number of tasks they can perform. – supercat Sep 12 '16 at 16:24
  • Maybe so, supercat. But my answer is about the logic behind what the standard requires (i.e. code that compiles with one standard-compliant implementation should not fail to compile with another standard-compliant implementation). If you think that premise needs to change, take it up with the standardisation committee - they agreed to make it work that way, not I. – Peter Sep 13 '16 at 11:56
  • Many tasks cannot be performed efficiently without using features which can be readily supported on 99% but not 100% of platforms. The early Standards Committees weren't trying to define everything a language needed in order to accomplish tasks efficiently on 90% of platforms, but merely the things that could be supported on 100%. They never "agreed" that such features shouldn't be supported on the 90% of platforms where doing so would be useful and cheap. – supercat Sep 13 '16 at 14:42
1

For the code to be guaranteed to work, it has to have some way to work without copy elision for every case where copy elision is not guaranteed.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
1

Because it was just permitted, but not guaranteed.

If an accessible copy constructor is not required, some code would compile when the optimization kicks in, but could fail on some other compiler.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Why the other compiler would not do the copy-elision, when it's allowed to happen? – Wake up Brazil Sep 12 '16 at 11:30
  • 1
    It could, but it was not required to. Perhaps it was "hard" to do in some cases? Anyway, the rule was there to allow a compiler to work either way. – Bo Persson Sep 12 '16 at 11:32
  • 1
    @WakeupBrazil The C++ standard committee is *very* cautious about what they decide to enforce. There could have been unforeseen cases where copy elision would be a pessimization, so they left the choice to the compiler. In C++17, with additional experience, they have decided that the chances of that happening are negligible, so copy elision will now be guaranteed where applicable. – Quentin Sep 12 '16 at 11:37
  • @BoPersson Richard Smith says this [here](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html): `The approach described herein achieves this not by eliding a copy, but by reworking the definition of the value categories (glvalue versus prvalue) such that it is unnatural to perform the copy in the first place.` I have some difficulty to understand how the "hard" part would disappear for such a compiler, just by changing the definition of some value categories in the Standard. – Wake up Brazil Sep 12 '16 at 11:39
  • @WakeupBrazil Well if something that was not before a prvalue is now a prvalue then you know you can steal it since it is a prvalue. Also something that was not an xvalue but is now an xvalue can also be safely elided. It is all about expressing intent and the new rules gives the compiler that intent. Before it had to do some static analysis to figure it out. At least that is my take on it. – NathanOliver Sep 12 '16 at 11:45
  • @Quentin If that was the case, why didn't they (language-lawyers) just suppress the last sentence in this Note in [class.copy]/32 in C++14: `[ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. — end note ]`. – Wake up Brazil Sep 12 '16 at 12:24
0

Why was it necessary (before the advent of "guaranteed copy elision") for the code to maintain a copy/move constructor even when copy-elision was permitted to happen?

Because as others have said, it was just permitted that the copy or move was omitted. Not every compiler had to omit it, so for consistency the programmer was still arrange for the copy/move to be possible. And conceptually, there was still a copy/move, whether it was carried out or not by the compiler is a different story.

Why does "guaranteed copy elision" free the programmer from these requirements?

Because there is no copy or move to begin with. The guaranteed copy "elision" works by completely changing the meaning of T a = T(), saying that T() initializes a instead of a temporary object, therefore at no point the copy or move constructors are even part of the game.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212