Search code examples
c++c++17copy-elision

Shouldn't guaranteed copy elision apply?


I do not understand the behavior of gcc here, I would expect RVO to apply, but regardless of whether I pass optimization flags and/or I pass -std=c++17, in the second case, the gratuitous pair of braces seems to prevent GCC from eliding the copy.

$ cat /tmp/foo.cc
#include <iostream>
#define PING() std::cerr << __PRETTY_FUNCTION__ << '\n'

struct foo
{
  foo() { PING(); }
  ~foo() { PING(); }
  foo(const foo&) { PING(); }
};

foo bar()
{
  PING();
  foo res;
  return res;
}

foo baz()
{
  PING();
  {
    foo res;
    return res;
  }
}

int main()
{
  foo f1 = bar();
  foo f2 = baz();
}
$ g++-mp-7 -std=c++17 -O3 foo.cc
$ ./a.out
foo bar()
foo::foo()
foo baz()
foo::foo()
foo::foo(const foo&)
foo::~foo()
foo::~foo()
foo::~foo()

Shouldn't this be part of the guaranteed copy elision? The behavior of Clang matches my expectations:

$ clang++-mp-4.0 foo.cc
$ ./a.out
foo bar()
foo::foo()
foo baz()
foo::foo()
foo::~foo()
foo::~foo()

Solution

  • From https://en.cppreference.com/w/cpp/language/copy_elision

    Under the following circumstances, the compilers are permitted, but not required to omit the copy- and move- (since C++11)construction of class objects even if the copy/move (since C++11) constructor and the destructor have observable side-effects. This is an optimization: even when it takes place and the copy-/move-constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.

    • If a function returns a class type by value, and the return statement's expression is the name of a non-volatile object with automatic storage duration, which isn't a function parameter, or a catch clause parameter, and which has the same type (ignoring top-level cv-qualification) as the return type of the function, then copy/move (since C++11) is omitted. When that local object is constructed, it is constructed directly in the storage where the function's return value would otherwise be moved or copied to. This variant of copy elision is known as NRVO, "named return value optimization".

    NRVO is not guaranteed to happen, but would be possible in your case.

    As pointed out by Sander De Dycker in a comment, there is already a bug report to get this opportunity of elision.