If my understanding is correct, the following is a classical circular dependency between template classes:
template <class MyB>
struct A {
MyB *b_;
};
template <class MyA>
struct B {
MyA *a_;
};
If we want to instantiate A with B and B with A, then we can't begin with either one, since we would have to write: A<B<A<B<...>>> (infinite).
I think that template template parameters provide a solution. The following code compiles (with gcc version 4.8.2):
template <class MyB>
struct A {
MyB *b_;
};
template <template <class> class MyA>
struct B {
MyA<B> *a_;
};
int main() {
using MyB = B<A>;
using MyA = A<MyB>;
MyA a;
MyB b;
a.b_ = &b; b.a_ = &a;
return 0;
}
Am I missing the essence of the problem?
UPDATE: I just ran into this post which proposes essentially the same solution.
I would try to design my code in order to avoid those circular dependencies. Anyway if prompted with a compelling reason not to, there are several ways of solve this issue:
As you noted, using a template template parameter solves the problem by breaking the cycle
template <class MyB>
struct A {
MyB *b_;
};
template <template <class> class MyA>
struct B {
MyA<B> *a_;
};
int main() {
using MyB = B<A>;
using MyA = A<MyB>;
MyA a;
MyB b;
a.b_ = &b; b.a_ = &a;
return 0;
}
Another solution could be to encapsulate the types you need into an external struct/class
template<class Common> struct A
{
typedef typename Common::BT B;
B* b;
};
template<class Common> struct B
{
typedef typename Common::AT A;
A* a;
};
struct Common {
using AT = A<Common>;
using BT = B<Common>;
};
int main() {
A<Common> a;
B<Common> b;
return 0;
}
Depending on what the rest of your code does, in a simple case like this you might get away with variadic templates
template <class MyA>
struct B;
template <typename ...MyB>
struct A {
B<A<>> *b_;
};
template <>
struct A<> {};
template <class MyA>
struct B {
A<B<MyA>> *a_;
};
int main() {
using BoA = B<A<>>;
using AoBoA = A<B<A<>>>;
BoA obj1;
AoBoA obj2;
obj1.a_ = &obj2;
obj2.b_ = &obj1;
return 0;
}
Finally it's worth to be noted that exposing a common base class and using a CRTP-like approach could probably be the cleaner way to achieve this (you might even gain points for clarity and readability).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With