Consider a closed1 class hierarchy such as the following:
class B {...};
class D1 final : public B {...};
class D2 final : public B {...};
Where B
is a abstract2 base class and D1
and D2
are its derived classes.
Due to implementation constraints or design, none of these classes have any virtual
methods but methods on B
that have different implementations in D1
and D2
are simply delegated by making a runtime check of the derived type, as follows:
class B {
bool isD1;
protected:
B(bool isD1) : isD1{isD1} {}
public:
std::string to_string() {
return isD1 ? static_cast<D1*>(this)->to_string() : static_cast<D2*>(this)->to_string();
}
}
class D1 final : public B {
public:
D1() : B(true) {}
std::string to_string() { // D1 specific implementation ... }
}
class D2 final : public B {
public:
D2() : B(false) {}
std::string to_string() { // D2 specific implementation ... }
}
Here the to_string
method on B
simply checks if the most-derived type of B
is D1
or D2
and calls the appropriate method (also called to_string
in both cases).
Cool.
Now imagine there are 10 more methods like B::to_string
. What can I do in C++11 to reduce the delegation boilerplate in B
, without resorting to macros?
In C++14 it seems a reasonable approach would be a generic delegation mechanism, like:
class B {
...
template <typename F>
auto delegate(F&& f) -> decltype(f(D1{})) {
return isD1 : f(*static_cast<D1*>(this)) : f(*static_cast<D2*>(this));
}
std::string to_string() {
return delegate([](auto&& b){ return b.to_string(); });
}
}
Here the [](auto&& b){ return b.to_string(); }
generic lambda works whether ultimately passed a D1
or D2
(since both have to_string
methods). In C++11 I don't see an equivalently concise way to express this.
Any ideas?
Of course, you could use macros to duplicate a non-generic macro and pass it to a 2-argument delegate
method (that takes separate functors for D1
and D2
) but I'd like to avoid macros.
1 Here closed means that the set of derived classes of B
is fixed and known at runtime.
2 Abstract in concept but not in the "pure virtual
" sense. That is this class should not be directly instantiated - the only entire objects that make sense are its derived classes. The various constructors are made protected
to enforce this.
Aucun commentaire:
Enregistrer un commentaire