c++c++20copy-elision

Can I avoid a second object when using a unary operator on a temporary?


The usual meaning of unary operators such as bitwise inversion, postfix increment, and unary minus is to return a modified copy of their argument. When their argument is a temporary, is there a way to modify that original object, thus avoiding the creation and destruction of a second object?

The examples below both involve two objects:

#include <utility>
#include <iostream>
using namespace std;

struct X {
  ~X()                    { cout << "destroy\n"; }
  X()                     { cout << "default construct\n"; }
  X(const X& )            { cout << "copy construct\n"; }
  X(      X&&)            { cout << "move construct\n"; }
  X& operator=(const X& ) { cout << "copy assign\n"; return *this; }
  X& operator=(      X&&) { cout << "move assign\n"; return *this; }
  X  operator~() const &  { cout << "~lvalue\n"; return *this; }
  X  operator~()       && { cout << "~rvalue\n"; return move(*this); }
};

int main(int, char**) {
  {
    cout << "Example 1\n";
    auto a = X();
    auto b = ~a;
  }
  {
    cout << "Example 2\n";
    auto a = ~X();
  }
}

I get this output (ignore that ~lvalue and copy construct "appear" out of order):

Example 1
default construct
~lvalue
copy construct
destroy
destroy
Example 2
default construct
~rvalue
move construct
destroy
destroy

Is there a way to rewrite the struct so that example 2 only creates and destroys a single object?


Solution

  • Is there a way to rewrite the struct so that example 2 only creates and destroys a single object?

    But your code says to create two objects. You create a prvalue and then perform an operation on it which is not initializing some other object. That operation requires manifesting a temporary from that prvalue. That's object 1.

    Then you use auto a to create a new object. That's object 2.

    It doesn't matter what operator~ is doing; you must manifest a temporary from the prvalue, since operator~ needs to have a this. Even if the return value is a && to this (which is absolutely should not, because operator~ should be a const function), that wouldn't help, because a is a separate object. You can move construct a, but it won't ever not be a separate object.

    If you only want one object, you have to use code that says to create only one object: auto a = X(); a.invert();.