Reputation: 5840
Consider the example.
I have a container class (A
), which overloads/implements all kinds of arithmetic operators (A::negate()
).
I now wish to create derived classes (B
and C
).
B
and C
should have all operators implemented by A
.
However, those operators should use derived-class objects as arguments.
The prototype for B::negate
should be: B B::negate()
, instead of the A B::negate()
.
The derived classes do not need any own fields, but may implement own methods (B::foo()
, C::bar()
).
It is a requirement that B
and C
be incompatible, i.e., a B
object can not be assigned to a C
object, or used with any of C
's operators.
Here is the example code, how I want it to work:
struct A {
int val;
A negate() {
return A{-val};
}
};
struct B: A {void foo(){}};
struct C: A {void bar(){}};
int main() {
B obj0 = {5};
B obj1 = obj0.negate();
}
I understand that this is probably impossible using standard inheritance, and might be something C++11 simply isn't capable of, so I'm asking for something as close as possible to it.
The currently best solution I've come up with involves not using inheritance at all, but instead adding an integer template parameter to the base class, defining derived classes as using B = A<1>;
,using C = A<2>;
, and implementing member methods only for some specializations (only A::foo<1>(){}
and A::bar<2>(){}
).
However, I'm highly unhappy with this solution.
Upvotes: 4
Views: 1113
Reputation: 275840
template<typename Child>
struct A {
Child* self() {
static_assert( std::is_base_of< A<Child>, Child >::value, "CRTP failure" );
return static_cast<Child*>(this);
}
Child const* self() const {
static_assert( std::is_base_of< A<Child>, Child >::value, "CRTP failure" );
return static_cast<Child const*>(this);
}
Child negate() {
return Child{-val};
}
};
struct B:A<B> {
explicit B(int v):A<B>(v) {}
};
here, we inject information into the base class template
about its child. B
is then relatively free to be a normal class.
In the parent, you can get self()
in order to access your this
pointer as a B
(or other derived class).
Another approach involves free functions. You write a free negate
template
function that checks if its argument is derived from A
, and if so does the negate action, and returns the negative version of the type passed in.
A mixture of these also works, where your free function takes A<D>
s and returns a D
.
Upvotes: 4
Reputation:
Covariant return types:
#include <iostream>
struct A {
virtual A& operator ! () { std::cout << "A" << std::endl; return *this; }
};
struct B : public A {
virtual B& operator ! () { std::cout << "B" << std::endl; return *this; }
};
int main() {
B b;
A* a = &b;
! *a;
}
Upvotes: 1
Reputation: 9434
If you don't want to use templates:
Make A::negate() protected.
In B:
struct B : public A
{
B & negate()
{
A:negate();
return *this
}
/// and so on
}
Because negate is defined in B, it totally hides the negate defined in A so the B implementation gets called and can delegate to A.
If you plan to hide ALL of A from the user, then B should contain an A rather than inheriting from it. (make A::negate public again)
struct B
{
private:
A m_a;
public:
B & negate()
{
m_a.negate();
return *this
}
/// and so on
}
Upvotes: 0