When does Return Value Optimization in C++ happen?

C++ has a feature called Return Value Optimization. I was curious when this does happen. So I created this copyelision.cpp file:

#include <iostream>;

class MyClass
{
public:
  MyClass()
  {
    std::cout << "MyClass [" << this << "] constructor called" << std::endl;
  }

  ~MyClass()
  {
    std::cout << "MyClass [" << this << "] destructor called" << std::endl;
  }

  MyClass(const MyClass &myClass)
  {
    std::cout << "MyClass [" << this << "] copy constructor called"
              << std::endl;
  }

  MyClass(MyClass &&myClass)
  {
    std::cout << "MyClass [" << this << "] move constructor called"
              << std::endl;
  }

  void setMyVariable(unsigned myVariable) { this->myVariable = myVariable; }

  unsigned getMyVariable() { return myVariable; }

private:
  unsigned myVariable = 0;
};

MyClass createMyClass()
{
  std::cout << "createMyClass() called" << std::endl;
  MyClass myClass;
  myClass.setMyVariable(97);

  return myClass;
}

MyClass createMyClass(bool value)
{
  if (value)
  {
    MyClass myClass;
    myClass.setMyVariable(1);
    return myClass;
  }
  MyClass myClass;
  myClass.setMyVariable(2);
  return myClass;
}

int main()
{
  std::cout << "Call createMyClass()" << std::endl;
  auto myClass1 = createMyClass();
  std::cout << "After createMyClass(), myClass1 variable is "
            << myClass1.getMyVariable() << std::endl;

  std::cout << "Call createMyClass(true)" << std::endl;
  auto myClass2 = createMyClass(true);
  std::cout << "After createMyClass(true), myClass2 variable is "
            << myClass2.getMyVariable() << std::endl;

  return 0;
}

The file contains a class MyClass that has the default constructor, copy constructor, move constructor and destructor defined, so that they can print to the terminal when they are called. Then there is a overloaded function createMyClass() that does create MyClass and return it by value. The overloaded version that takes a bool value as an argument, creates the object in different branches.

I first compiled the file with Clang (clang++ copyelision.cpp) and then with GCC (g++ copyelision.cpp).

This was the output from Clang:

Call createMyClass()
createMyClass() called
MyClass [0x7ffd7978a380] constructor called
After createMyClass(), myClass1 variable is 97
Call createMyClass(true)
MyClass [0x7ffd7978a378] constructor called
After createMyClass(true), myClass2 variable is 1
MyClass [0x7ffd7978a378] destructor called
MyClass [0x7ffd7978a380] destructor called

And this from GCC:

Call createMyClass()
createMyClass() called
MyClass [0x7ffe6f029560] constructor called
After createMyClass(), myClass1 variable is 97
Call createMyClass(true)
MyClass [0x7ffe6f029524] constructor called
MyClass [0x7ffe6f029564] move constructor called
MyClass [0x7ffe6f029524] destructor called
After createMyClass(true), myClass2 variable is 0
MyClass [0x7ffe6f029564] destructor called
MyClass [0x7ffe6f029560] destructor called

It is really interesting to see that Clang does not make a copy or move the object in any case. GCC does also no copying or moving on the non branching code. However on the branching code, GCC does call the move constructor on returning the value. Even with optimization turned on (-O3), GCC seems not to be able to optimize that code.

With Clang as a compiler, it seems to be save to write functions or methods, that return objects by value. No need to define functions like void createMyObject(MyObject &obj).

The code was compiled on x86_64 Linux with version 9.0.1 of Clang and version 9.3.0 of GCC.