C++:当两个 类 包含指向另一个的指针时,如何解决派生 类 之间的模板循环依赖?
C++: How to solve template cyclic dependency between derived classes when two classes contain a pointer pointing to another?
我有一个父 class 和一些从它派生的 class。我想要 'pair' 两个派生的 classes,每个 eac 都有一个指向另一个的指针。
代码示例:
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;
我知道我可以用虚函数做到这一点,但我试图找到一种使用模板的方法。
我从 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;
}
但它只适用于两个固定的 classes A 和 B。考虑到我有很多派生的 classes 并且我想 'pair' 其中的任何两个,我怎么能解决这个问题?
更新1.修复第一个代码块中的拼写错误
更新 2。我尝试了以下代码
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;
}
它编译并打印了B A
。但是这段代码正确吗?有没有副作用?
DerivedB
不是 class;这是一个模板。你不能只有 DerivedA<DerivedB>
,因为那没有意义。 DerivedA
里面是什么DerivedB
?如果你这样想,你就会看到问题:你的两个变量的类型是无限的:一个是DerivedA<DerivedB<DerivedA<DerivedB<...>>>>
,另一个是DerivedB<DerivedA<DerivedB<DerivedA<...>>>>
。你不能有无限类型。您 必须 在某处使用包装器 class 来打破循环。这种情况的通用包装器类型是
template<template<typename> typename F, template<typename> typename... Fs>
struct Fix {
F<Fix<Fs..., F>> unwrapped;
};
Fix<F1, F2, ..., Fn>
代表F1<F2<...<Fn<F1<F2<...>>>>...>>
。你可以这样得到你的两个对象:
Fix<DerivedA, DerivedB> dA;
Fix<DerivedB, DerivedA> dB;
dA.unwrapped.prtToPair = &dB;
dB.unwrapped.prtToPair = &dA;
类似下面的内容?
#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;
}
它使这两种类型依赖于它们关联的 Combo
,并且选择正确的 other_type
成为 Combo
实现的一部分。请注意,Combo<DerivedA, DerivedB>
和 Combo<DerivedB, DerivedA>
现在将导致不同的类型。
关于您的修改:
通过 reinterpret_cast
返回的指向不相关类型的指针访问值或使用它调用非静态成员函数(正如您正在做的那样)会导致未定义的行为。
我有一个父 class 和一些从它派生的 class。我想要 'pair' 两个派生的 classes,每个 eac 都有一个指向另一个的指针。
代码示例:
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;
我知道我可以用虚函数做到这一点,但我试图找到一种使用模板的方法。
我从 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;
}
但它只适用于两个固定的 classes A 和 B。考虑到我有很多派生的 classes 并且我想 'pair' 其中的任何两个,我怎么能解决这个问题?
更新1.修复第一个代码块中的拼写错误 更新 2。我尝试了以下代码
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;
}
它编译并打印了B A
。但是这段代码正确吗?有没有副作用?
DerivedB
不是 class;这是一个模板。你不能只有 DerivedA<DerivedB>
,因为那没有意义。 DerivedA
里面是什么DerivedB
?如果你这样想,你就会看到问题:你的两个变量的类型是无限的:一个是DerivedA<DerivedB<DerivedA<DerivedB<...>>>>
,另一个是DerivedB<DerivedA<DerivedB<DerivedA<...>>>>
。你不能有无限类型。您 必须 在某处使用包装器 class 来打破循环。这种情况的通用包装器类型是
template<template<typename> typename F, template<typename> typename... Fs>
struct Fix {
F<Fix<Fs..., F>> unwrapped;
};
Fix<F1, F2, ..., Fn>
代表F1<F2<...<Fn<F1<F2<...>>>>...>>
。你可以这样得到你的两个对象:
Fix<DerivedA, DerivedB> dA;
Fix<DerivedB, DerivedA> dB;
dA.unwrapped.prtToPair = &dB;
dB.unwrapped.prtToPair = &dA;
类似下面的内容?
#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;
}
它使这两种类型依赖于它们关联的 Combo
,并且选择正确的 other_type
成为 Combo
实现的一部分。请注意,Combo<DerivedA, DerivedB>
和 Combo<DerivedB, DerivedA>
现在将导致不同的类型。
关于您的修改:
通过 reinterpret_cast
返回的指向不相关类型的指针访问值或使用它调用非静态成员函数(正如您正在做的那样)会导致未定义的行为。