Reputation: 428
I know this seems convoluted, but I hope somebody may point out my mistake here. I'm sure the design will leave a bad taste in some of your mouths, and I'm happy to hear design alternatives. But the form comes from modeling in the problem domain.
I'm sure I'm being silly, but there is something about the way templates couple with inheritance that is confusing me. Here is some pared down code with a description to follow:
// main.cpp
#include <cstdio>
template <typename POD>
class A {
public:
POD data;
POD get_a() { return data; }
};
template <typename T>
class B {
public:
virtual void do_something_with_an_A_child(const T&) = 0;
};
class ADerived : public A<float> {
public:
ADerived(float a) { data = a; }
};
class BDerived : public B<ADerived> {
public:
virtual void do_something_with_an_A_child(const ADerived& someA) {
printf("A bit of A-type's data: %f\n", someA.data);
}
};
template <typename POD>
class Aggregator {
public:
A<POD>* instanceOfA;
B<A<POD>>* instanceOfB;
Aggregator(A<POD>* anA, B<A<POD>>* aB) : instanceOfA(anA), instanceOfB(aB) {}
};
int main() {
ADerived myADerived(3.14159f);
BDerived myBDerived;
Aggregator<float> myAggregator(&myADerived, &myBDerived);
return 1;
}
So A
is a class template that's signature is just some built-in type(s). (In my particular case, A
has methods which operate on its internal data.) The class B
is meant to promise an interface to some operations that are meant for a specific instance of an A<.>
. So, in the code above, you see I define A
and B
as template classes and then create classes which inherit from those template classes, giving ADerived
and BDerived
.
(In the real case, A
represents a mathematical model and some data, and B
is an interface to perform a mathematical optimization on a model.)
Now, an class which should have links to instances of an ADerivded
and BDerived
(for the purposes of creating a generic algorithm based on their interfaces) I have called Aggregator
, which simply takes some pointers in the constructor. I only want Aggregator
to need to know about the POD
types, since it is meant only to manipulate the interfaces and not worry too much about what the objects contain.
I get the following compiler error from clang++
with -std=c++14
:
main.cpp:42:21: error: no matching constructor for initialization of 'Aggregator<float>'
Aggregator<float> myAggregator(&myADerived, &myBDerived);
^ ~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:35:3: note: candidate constructor not viable: no known conversion from 'BDerived *' to 'B<A<float> > *' for 2nd argument
Aggregator(A<POD>* anA, B<A<POD>>* aB) : instanceOfA(anA), instanceOfB(aB) {}
^
The meaning is clear, but I'm not sure why it can't see that a BDerived
is a B<A<float>>
. I mean, it is specifically a B<ADerived>
, but ADerived
is an A<float>
, and therefore a BDerived
should be seen as a B<A<float>>
. This obviously isn't the case, so either I'm doing something stupid and hope to have someone point it out, or this isn't possible in C++, and I need some alternative design suggestions.
Upvotes: 0
Views: 58
Reputation: 10880
Template arguments are resolved statically and to exact type. Thus B<ADerived>
won't decay to B<A<POD>>
even if ADerived
is a descendant of A<POD>
. You might work around this by:
T
, in Aggregator
, and optionally ensuring (using std::enable_if<>
and std::is_base<>
) that A<POD>
is a base of T
, orT
in B
using a typedef
, then taking T
in Aggregator
and storing T
and A<B::T>
in Aggregator
.Upvotes: 2