I found this construct some time ago. It took some reading to understand why it worked. I’m still not sure if it is actually legal, or just works only because m_derivedData is not accessed in Base::Base.
1 2 3 4 5 6 7 8 9 10 11 |
struct Base { std::string& m_derivedData; Base(std::string& data) : m_derivedData(data) { } }; struct Derived : public Base { std::string m_data; struct Derived() : Base(m_data), m_data("foo") { } }; |
m_derivedData will point to uninitialized memory in Base::Base(), so using it fails, as can be trivially seen in valgrind.
There does not seem to be any way to initialize m_derivedData such that it is valid in Base::Base when compiling with g++, at least, not sure why, I’d expect initializing m_data first to work.
There is nothing wrong with this. m_derivedData is a reference, and it is bound to a region of data storage, not a value. Therefore, it doesn’t matter whether that region of data storage is initialised or not.
Having references/pointers to yourself is dangerous. It means your default copy/move constructor are not working properly, and most people forget to define a custom move/copy constructor to fix this. So this gets discovered the hard way.
And the solution isn’t trivial: Base can’t define a copy/move-constructor that works, so it must be non-copyable non-movable, and Derived must implement a move and copy-constructor that works around Base’s limitations.