c – 使用模板模板参数打破模板循环依赖关系

如果我的理解是正确的,以下是模板类之间的经典循环依赖:

template <class MyB>
struct A {
    MyB *b_;
};

template <class MyA>
struct B {
    MyA *a_;
};

如果我们想要用A来实例化A和B,那么我们就不能从任何一个开始,因为我们必须写:A< B< B< B<>>>> (无穷).

我认为模板模板参数提供了一个解决方案.以下代码编译(使用gcc版本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;
}

我错过了问题的本质吗?

更新:我刚刚进入this帖子,提出了基本相同的解决方案.

最佳答案 我会尝试设计我的代码,以避免这些循环依赖.无论如何,如果有令人信服的理由提示,有几种方法可以解决这个问题:

>如您所述,使用模板模板参数可以通过打破循环来解决问题

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;
}

>另一种解决方案可能是将您需要的类型封装到外部结构/类中

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;
}

>根据代码的其余部分,在这样的简单情况下,您可能会使用可变参数模板

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;
}

最后值得注意的是,暴露一个公共基类并使用类似CRTP的方法可能是实现这一目标的更简洁方法(您甚至可能获得清晰度和可读性的分数).

点赞