3

我有一个父类和一些从它派生的类。我想“配对”两个派生类,其中 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;
}

但它只适用于两个固定类 A 和 B。考虑到我有很多派生类,我想“配对”其中的任何两个,我该如何解决这个问题?

更新 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。但是这段代码正确吗?它有任何副作用吗?

4

2 回答 2

1

DerivedB不是一个类;这是一个模板。你不能只拥有一个DerivedA<DerivedB>,因为那没有意义。里面是什么样的 ?如果你这样想,你就会看到问题:你的两个变量的类型是无限的:一个是,另一个是。你不能有一个无限的类型。您必须在某处使用包装类来打破循环。这种情况的通用包装器类型是DerivedBDerivedADerivedA<DerivedB<DerivedA<DerivedB<...>>>>DerivedB<DerivedA<DerivedB<DerivedA<...>>>>

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;
于 2019-12-15T06:00:00.310 回答
1

像下面这样的东西?

#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>andCombo<DerivedB, DerivedA>现在将导致不同的类型。


关于您的编辑:

通过指向reinterpret_cast不相关类型的指针访问值或使用它调用非静态成员函数(如您所做的那样)会导致未定义的行为。

于 2019-12-15T06:08:43.963 回答