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.