I have a class A
that has a data member data
of type base_data
. I would like to extend A
to B
by inheritance, but at the same time extend A::data
to a derived type derived_data
.
One possible design for that is to declare A
as a template class, accepting as parameter the type of A::data
.
struct base_data {
};
struct derived_data : public base_data {
};
template <class DataType = base_data>
struct A {
DataType data;
};
struct B : public A<derived_data> {
};
The problem with this design is that, strictly speaking, B
is not derived from A<>
, hence functions that involve an A<>
object would not compile with a B
type. Example:
void f(A<>&&) { }
void f(const A<>&) { }
A x, y;
f(x);
f(std::move(y));
B xB, yB;
f(xB); // Error
f(std::move(yB)); // Error
One workaround is to declare f
as template function
template <class T>
void f(A<T>&&) { }
template <class T>
void f(const A<T>&) { }
This however has the drawback that f
can be called with any A<T>
, which may not be desirable.
Another approach would be to constraint the argument of f
to be either A<>
or B
template <typename T, class = std::enable_if_t<std::is_same_v<T, A<>> || std::is_same_v<T, B>>>
void f(const T& a) { }
template <typename T, class = std::enable_if_t<std::is_same_v<T, A<>> || std::is_same_v<T, B>>>
void f(A<>&& a) { }
This is also not very elegant. Essentially, the part of a code that contains functions using A
now must know that it could also use a sort-of-derived class B
, perhaps defined elsewhere. Also, if there are many of such functions, one has to modify all of them to template functions as above. And if another sort-of-derived class C is added, one has to update all declarations of f
and similar.
A third option would be to provide explicit conversions from B
to A<>
template <class DataType = base_data>
struct A {
DataType data;
A() { }
A(B&&) { }
A(const B&) { }
};
But again, this implies that the code of A
must know its sort-of-descendant B
. Even worse, the copy conversion A(const B&)
may be expensive and defies the simple idea that a const reference can bind to a derived class, without additional overhead.
Is there a good/better design pattern for this problem?
Aucun commentaire:
Enregistrer un commentaire