问题
I have a parent class and some classes derived from it. I want to 'pair' two derived classes that eac has a pointer to another one.
Code example:
template<typename DerivedClassName>
class Parent {
// some stuff
DerivedClassName* prtToPair;
};
template<typename DerivedClassName>
class DerivedA : public Parent<DerivedClassName> {
};
template<typename DerivedClassName>
class DerivedB : public Parent<DerivedClassName> {
};
// compile fails
DerivedA<DerivedB> dA;
DerivedB<DerivedA> dB;
dA.prtToPair = &dB;
dB.prtToPair = &dA;
I know I can do this with virtual function but I try to find a way to use template.
I found a solution from http://qscribble.blogspot.com/2008/06/circular-template-references-in-c.html:
#include <stdio.h>
template<class Combo> struct A
{
typedef typename Combo::b_t B;
B* b;
};
template<class Combo> struct B
{
typedef typename Combo::a_t A;
A* a;
};
struct MyCombo {
typedef A<MyCombo> a_t;
typedef B<MyCombo> b_t;
};
int main(int argc, char* argv[])
{
A<MyCombo> a;
B<MyCombo> b;
a.b = &b;
b.a = &a;
return 0;
}
but it only works for two fixed classes A and B. Consider I have many derived classes and I want to 'pair' any two of them, how can I solve this problem?
Update 1. fix a typo in first code block Update 2. I tried following code
template<typename DerivedClassName>
class Parent {
// some stuff
public:
DerivedClassName *prtToPair;
};
template<typename DerivedClassName>
class DerivedA : public Parent<DerivedClassName> {
public:
void func() {
std::cout << "A" << std::endl;
}
};
template<typename DerivedClassName>
class DerivedB : public Parent<DerivedClassName> {
public:
void func() {
std::cout << "B" << std::endl;
}
};
int main() {
DerivedA<DerivedB<void>> A;
DerivedB<DerivedA<void>> B;
A.prtToPair = reinterpret_cast<DerivedB<void> *>(&B);
B.prtToPair = reinterpret_cast<DerivedA<void> *>(&A);
A.prtToPair->func();
B.prtToPair->func();
return 0;
}
It compiled and printed B A
. But is this code correc? Does it have any side effect?
回答1:
Something like the following?
#include <type_traits>
template <typename Combo>
struct Parent {
// some stuff
typename Combo::other_type* prtToPair;
};
template <typename Combo>
class DerivedA : public Parent<Combo> {};
template <typename Combo>
class DerivedB : public Parent<Combo> {};
template <template <typename...> class T, template <typename...> class U>
struct Combo {
private:
template <typename Combo, bool B>
struct impl {
using other_type =
typename std::conditional_t<B, typename Combo::type2, typename Combo::type1>;
};
public:
using type1 = T<impl<Combo, true>>;
using type2 = U<impl<Combo, false>>;
};
int main() {
using C = Combo<DerivedA, DerivedB>;
using A = typename C::type1;
using B = typename C::type2;
A dA;
B dB;
dA.prtToPair = &dB;
dB.prtToPair = &dA;
}
It makes the two types dependent on the Combo
they are associated with and the choice of the correct other_type
is made part of the implementation of Combo
. Note that Combo<DerivedA, DerivedB>
and Combo<DerivedB, DerivedA>
will now lead to different types, though.
Regarding your edit:
Accessing a value through the pointer returned by reinterpret_cast
to an unrelated type or calling a non-static member function using it (as you are doing) causes undefined behavior.
回答2:
DerivedB
isn't a class; it's a template. You can't just have a DerivedA<DerivedB>
, because that doesn't make sense. What kind of DerivedB
is inside DerivedA
? If you think that way, you see the problem: the types of your two variables are infinite: one of them is DerivedA<DerivedB<DerivedA<DerivedB<...>>>>
and the other is DerivedB<DerivedA<DerivedB<DerivedA<...>>>>
. You can't have an infinite type. You must use a wrapper class somewhere to break the cycle. A generic wrapper type for this situation is
template<template<typename> typename F, template<typename> typename... Fs>
struct Fix {
F<Fix<Fs..., F>> unwrapped;
};
Fix<F1, F2, ..., Fn>
represents F1<F2<...<Fn<F1<F2<...>>>>...>>
. You can get your two objects as so:
Fix<DerivedA, DerivedB> dA;
Fix<DerivedB, DerivedA> dB;
dA.unwrapped.prtToPair = &dB;
dB.unwrapped.prtToPair = &dA;
来源:https://stackoverflow.com/questions/59341379/c-how-to-solve-template-cyclic-dependency-between-derived-classes-when-two-cl