Don't rely on constructor side effects

If you're using C++ and relying on side effects of constructors, you will possibly find yourself scratching your head at one point in time. Consider the following code:

#include <iostream>

using std::cout;

struct Payload
{
};

struct Klass
{
    Payload &p_;

    Klass(Payload &p) : p_(p) { cout << "1"; }
    Klass(const Klass &k) : p_(k.p_) { cout << "2"; }
    Klass(Klass &&k) : p_(k.p_) { cout << "3"; }
    ~Klass() { cout << "4"; }
};

int main(int argc, char *argv[]) {
    Payload *p = new Payload();

    Klass T1(*p);
    Klass T2(T1);
    Klass T3((Klass(*p)));

    delete p;

    return 0;
}

Now, what would you expect the output to be? If you're as naive as I first was you will probably expect the output to be 12134444.

This does seem like an obvious answer. Chances are, though, that you will be surprised if you actually try to run this code, for the output will be 121444.

What is going on here? Is the compiler forgetting about our T3? Why would it still go ahead and construct the temporary then?

While searching for an answer to those questions I eventually stumbled upon this article about copy elision, which explains what is going on:

The compiler will optimize the constructor call away and construct the temporary in T3's storage instead.

Despite having read about those optimizations before, I forgot about them. It is always nice to be reminded of important details like this.

Forgetting about things for a few times and having to rework the same problems will eventually help you remember stuff better.