C++ copy elimination

What does the following C++ program print?

class A {
  public:
    A()           { cout << "A()" << endl; }
    A(const A &a) { cout << "A(A)" << endl; }
};
A g() { A a; return a; }
int main() { A a(g()); }

The answer: it depends on what compiler you’re using. You might expect:

A()
A(A)

With most full-featured C++ compilers, however (including GCC), the constructor is called once:

A()

This turns out to be an “optimization” that is explicitly mention in the language standard. It’s one for which C++ implementations are allowed to forgo the straightforward semantics. From section 12.8.14 of the C++98 standard:

Whenever a temporary class object is copied using a copy constructor, and this object and the copy have the same cv-unqualified type, an implementation is permitted to treat the original and the copy as two different ways of referring to the same object and not perform a copy at all, even if the class copy constructor or destructor have side effects. For a function with a class return type, if the expression in the return statement is the name of a local object, and the cv-unqualified type of the local object is the same as the function return type, an implementation is permitted to omit creating the temporary object to hold the function return value, even if the class copy constructor or destructor has side effects. In these cases, the object is destroyed at the later of times when the original and the copy would have been destroyed without the optimization.

This is described more elsewhere in the spec, with examples (e.g. 12.2.2). The MSDN Library also has an article on named return value optimization.

In the end, it’s nice to be able to write more expressive/higher-level-looking code without worrying about unnecessary copies being made. However, you may have to pay attention to certain cases where your ctors/dtors have side effects you’re relying on, and it may be subtle to figure out when copies are or aren’t actually being eliminated.

More experimentation with this feature can be found in this source file, part of my ever-growing sandbox of toy code.

Thanks to SIPB and ##c++ for the discussion.

Follow me on Twitter for stuff far more interesting than what I blog.