Changing element types in inheritance
Given a base class Base
that has two derived classes, DerA
and DerB
, can derived classes have a member variable that is used in a member function Base
, but for each type of class?
class Base {
* a // Declare a as *something*, so it can be used by "doWork"
template <typedef T>
void doWork(T b) { // Add another value to "a" which is of its same type
a += b; // For example; an operation that works on "a", no matter what numeric type it is
}
}
class DerA : public Base {
// Make "a" an int
}
class DerB : public Base {
// Make "a" a float
}
In practice, it a
will be a base structure, and DerA
they DerB
will have derived versions of the base structure (derived classes will have a derived form of a structure specific to their purpose, but everyone has to do a simple operation on a
, so it seems pointless to copy / paste this simple function for each derivative when I can just use the template function). I would just a
type struct as the base type, but then I lose access to the various specialized functions and variables that each derived struct has (if I understand inheritance correctly).
I apologize if this question is repeated, but I do not know what this quality will be called, so Googling has proven fruitless.
source to share
What you might need is CRTP .
template<class D>
struct Base {
D* self() { return static_cast<D*>(this); }
D const* self() const { return static_cast<D*>(this); }
template<class T>
void doWork(T b) {
self()->a += b;
}
};
struct DerA : public Base<DerA> {
int a;
};
struct DerB : public Base<DerB> {
double a;
};
Here we are passing the derived type to our base class. In the base class, you can use self()->
to access derived type fields. This basically allows full access to the derived type, allowing us to share code in the base class.
Please note that you cannot transfer to DerA
and DerB
around like Base
this. If you want that, you need a method virtual
doWork
, and methods virtual
template
don't exist.
CRTP stands for Curiously Repeating Pattern Pattern, which I think is named because it's weird, it involves type repetition, and it keeps showing up in strange corners as useful.
Erase type probably won't work either, since you want to send a type erase from two different places in your code base (double dispatch problem: you need a centralized list of types supported to work with a Cartesian type).
To expand this, to support a+=b
where both a
and b
are arbitrary types, you would have to expand all types twice, including types that will never be mutually visible in the same place in the compilation unit. It's impossible.
If you want a generic base and there are only a few types that you transition to doWork
, here's how you do it:
struct Base {
virtual void doWork( double ) = 0;
virtual void doWork( int ) = 0;
virtual void doWork( long long ) = 0;
};
template<class D>
struct Base_helper:Base {
D* self() { return static_cast<D*>(this); }
D const* self() const { return static_cast<D*>(this); }
template<class T>
void doWork_impl(T b) {
self()->a += b;
}
void doWork( double x ) override { doWork_impl(x); };
void doWork( int x ) override { doWork_impl(x); };
void doWork( long long x ) override { doWork_impl(x); };
};
struct DerA : public Base_helper<DerA> {
int a;
};
struct DerB : public Base_helper<DerB> {
double a;
};
note that each version doWork
must be valid to call each of the Der
s as it Base_helper
creates all of them.
If the type of the type passed to doWork
is unbounded, still the types are Der
limited, you can do something like above just backwards. However, this becomes inconvenient. It is best to use a solution like boost::variant
.
source to share
I think you want to achieve something like this:
template<typedef T>
class Base {
T a;
void doWork(T b) { // Add another value to "a" which is of its same type
a += b; // For example; an operation that works on "a", no matter what numeric type it is
}
}
class DerA : public Base<int> {
}
class DerB : public Base<float> {
}
Or you can dump the DerA and DerB classes entirely and use typedefs:
typedef Base<int> DerA;
typedef Base<float> DerB;
source to share
This can be easily solved with a CRTP-like pattern:
template<class D> // Base class is templated
class Base {
public:
D a;
void doWork(D b) {
a += b;
}
};
class DerA : public Base<int> {};
class DerB : public Base<float> {};
Edit: in case you only need one common base ( Base<int>
- a completely different type from Base<float>
), you can use the interface class and inherit Base from it.
source to share