Order of members matters always.
The presence of the smart pointer is a red hering. The crux in your example is just that initialization of one member depends on some other member already being initialized.
Members are initialized in the order they appear in the class definition. Always.
Even if you list them in different order in the member initializer list, they are still initialized in the order they appear in the class definition. And usually compilers warn when the order is different.
You get similar issue with:
struct foo {
int x;
int y;
foo() : x(1),y(this->x * 2) {}
};
Changing the order of x
and y
would render the initialization undefined (y
would use uninitialzed x
).
But is this design not somewhat brittle?
Yes it is. You need to be extra careful when initialization of members depends on each other.
What if someone changes the member order?
You will get a compiler warning, that you better not ignore.
Is there a better/canonical design where there is no need of relying on the member order, or is it just like it is with RAII?
You probably do not need a reference and a smart pointer. Get rid of one of them. As they are both public
there is really no point in having them both.
In general, what was suggested in a comment may be a solution. If you refactor one member to be member of a base class then there is no doubt about order of initialization, because base classes are initialized first.