A Manual Setters Overloading Solution
One cure is to manually overload all the inherited setters in the derived options class,
changing the return types to "follow" the class, that is, with covariant return types:
...
struct CheckBox: AbstractButton
{
struct Params: AbstractButton::Params
{
bool isAutoValue;
Params(): isAutoValue( true ) {}
int hTextAlign() const
{ return AbstractButton::Params::hTextAlign(); }
Params& hTextAlign( int v )
{
return static_cast<Params&>(
AbstractButton::Params::hTextAlign( v )
);
}
bool isAuto() const { return isAutoValue; }
Params& isAuto( bool v ) { isAutoValue = v; return *this; }
};
explicit CheckBox( Params const& = Params() )
: AbstractButton( params )
{}
};
The reason that also the hTextAlign getter, not only the setter, is overloaded here, is that any hTextAlign overload hides or "shadows" the base class implementations. This hiding rule is so perplexing to C++ novices that it has its own FAQ entry, but without it the setter overloading couldn't work since one can't overload solely on return type (which brings in a terminology issue: is "overload" the right word?).
Why Manual Setters Overloading Can Be Impractical
Manually implemented setters work, but the source code size overhead of introducing a single new option in a derived class can be quite large when the base class has many options. And when a new option is introduced in a base class, or an option is renamed or removed, then all the derived classes' options classes have to be manually updated. If a base class has n options, and has a chain of d derivations down to some class, each of which introduces 1 new option, then the number of setter implementations for that chain is (d+1)·m + (d2+d)/2. If every class in that chain of n = d+1 classes defines m options, then the number of setter implementations is m·(n2+n)/2. And even though we're talking very small numbers here this means that the manual setters overloading approach can often be wholly impractical.


