3

What is the reason the C++ standard allows(requires) compilers to optimize away calls to copy constructor(in certain cases), even though it might contain observable side effects?

If I'm not mistaken, the "as if" rule already allows compilers to optimize unnecessary code away, as long as the resulting program emulates the observable behavior of the abstract machine defined in the standard.

What were the motives behind making an exception rule? Doesn't it create inconsistencies in the language? Is it convenient(or necessary)?

TylerH
  • 20,799
  • 66
  • 75
  • 101
wolfofuniverse
  • 441
  • 4
  • 8
  • Are you asking about the "allows" part, or about the relatively new "requires" part? They may seem similar but we can probably invoke different arguments. In particular, guaranteed elision isn't _really_ elision: it's just a change to the language's rules of "when an object is actually created", such that there is really no inconsistency at all. – Asteroids With Wings Nov 29 '20 at 21:57
  • @AsteroidsWithWings Seems like my comment is an opinion more than an answer. Edit : Or at least, my guess as to the reasoning for the rule. Edit 2 : Moved it. – François Andrieux Nov 29 '20 at 21:59

2 Answers2

7

The vast majority of the time, the copy generated by the return would be a needless cost. The rest of the time, it is expected that the copy constructor's side effects would be undone by the destruction of the copy. Things like std::shared_ptr do work on copy that can be observed externally, but undoes it on destruction. It is extremely rare for an object's copy construction to have a side effect that would be missed by a copy elision. It's worth it to avoid the performance hit to inconvenience the rare case. It basically is never problematic.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
1

Copy elision can mean two different things. The "mandatory copy elision" in C++17 is simply a redefinition of what a prvalue is.

auto foo() { return std::mutex{}; }

This is legal and makes sense because within the function std::mutex isn't "created" yet as far as the language is concerned. Let's not dwell on this case.

In other cases, such as

auto bar()
{
    std::vector v{1, 2, 3};
    return v;
}

the compiler is allowed to elide the copy/move, after checking that copy/move semantics are legal.

Doesn't it create inconsistencies in the language?

Yes. But there is rarely a language as fixated on consistency as C++, copy elision stands out precisely because C++ prizes consistency. This compromise is made for the overwhelmingly large benefit: we don't want people to worry about performance overhead for writing functions.

The as-if rule is sometimes theoretically enough to generate efficient code, but is often extremely difficult in practice. Compilers have trouble optimizing this for the longest time

delete new int;

trivial as it may be for a human to reason about. We don't want to wait until optimizers are perfect before starting to write functions.

Passer By
  • 19,325
  • 6
  • 49
  • 96