This may come a little late after other answers, but I'll still give it a try.
You can implement this safely, and without changing derived classes. However, you will need to change use of all these classes, which might be far worse, depending on your scenario. If you are still designing, then this might be viable alternative.
Basically, you can apply the curiously recurring template pattern and inject the initialization code after the constructor gets invoked. Furthermore, if you do it as I've written it below, you can even protect load
from being called twice.
struct Loader {
int get(int index) { return 0; }
};
struct Base {
virtual ~Base() {} // Note: don't forget this.
protected:
virtual void load( Loader & ) = 0;
};
struct Derived : public Base {
int value;
protected:
void load( Loader &l ) {
value = l.get(0);
}
};
template<typename T>
class Loaded : public T
{
public:
Loaded () {
Loader l; T::load(l);
}
};
int main ( int, char ** )
{
Loaded<Derived> derived;
}
Frankly, though, I would consider an alternate design if you can. Move the code from load
to your constructors and provide the loader as an a reference argument defaulting as follows:
struct Derived : public Base {
Derived ( Loader& loader = Loader() ) { ... }
};
That way, you completely avoid the problem.
Summary: your choices are the following:
- If you are not limited by external constraints and don't have an extensive code base depending on this, change your design for something safer.
- If you want to keep
load
the way it is and not change your classes too much but are willing to pay the price of changing all instantiations, apply CRTP as proposed above.
- If you insist on being mostly backward compatible with existing client code, you will have to change you classes to use a PIMPL as others have suggested or live with the existing problem.