多个模板参数参考循环

时间:2018-12-12 13:38:49

标签: c++ templates

我有两个类,一个B类具有一个成员A1,而A1类具有一个引用成员B,这很容易这样编写:

class B;

class A1 {
 public:
  A1(const B& b) : b_(b) {}
 private:
  const B& b_;  // a reference to B
};

class B {
 public:
  B(A1 a1) : a1_(a1) {}
 private:
  A1 a1_;  // a Type A1 member 
};

然后我需要将类B更改为模板类,因此代码应如下所示:

template<typename T1>
class B;


class A1 {
 public:
  A1(const B<A1> &b) : b_(b) {};
 private:
  const B<A1> &b_; // a reference to B<A1>
};

template<typename T1>
class B {
 public:
  B(T1 t1) : t1_(t1){}
 private:
  T1 t1_; //a T1 type member
};

突然之间,我们在B中需要第二个成员,所以我将B更改为:

template<typename T1, typename T2>
class B {
 public:
  B(T1 t1, T2 t2) : t1_(t1), t2_(t2){}
 private:
  T1 t1_;
  T2 t2_;
};

这有问题:

现在,如果我是A的编码员,则以下代码是非法的:

template<typename T1, typename T2>
class B;


class A1 {
 public:
  A1(const B<A1> &b) : b_(b) {}; //wrong, need a second template argument
 private:
  const B<A1> &b_; // wrong, either
};

这意味着编码人员必须填写他可能不知道(或至少可能不在乎)的第二个模板参数。所以我将代码更改为此:

template <typename T1, typename T2>
class B;

template <typename TB>
class A1 {
 public:
  explicit A1(const TB& b) : b_(b) {}
 private:
  const TB& b_;
};

template <typename TB>
class A2 {
 public:
  explicit A2(const TB& b) : b_(b) {}
 private:
  const TB& b_;
};

template <typename T1, typename T2>
class B {
 public:
  B(T1 a1, T2 a2) : a1_(a1), a2_(a2) {}
 private:
  T1 a1_;
  T2 a2_;
};

这看起来不错,除了我不知道如何制作B实例(像这样:B<A1<B<A1<...>,A2>, A2>,它是递归的。)

我期望的理想设计是A1A2的程序员根本不需要彼此了解,而B的程序员只需添加{{ 1}}和A1差不多A2(或添加一些其他内容),即使他添加了B<A1, A2> A1 {{1} } A2`无需更改。

所以

1:如果我坚持使用这种语法,是否表示我必须放弃使用T3的模板,而是使用指针?

2:如果我坚持使用模板,这是否意味着类B<A1, A2, A3>, the code of和类and必须彼此了解?

3:在BT1独立的情况下,有没有第三种使用模板的方法?也就是说,无论我向类T2添加了多少个参数,T1类都不需要更改?模板模板/ CRTP / SFINAE,所有这些似乎都没有用。由于T2类仅是类B中的引用,因此A1类更像是一个接口,而不是某个类,这使我想起了C ++提案“ concept”,可以在这种情况下有“概念”帮助吗?

4:这是xy问题吗?那意味着我不应该以这种方式设计我的代码模式吗?

2 个答案:

答案 0 :(得分:1)

我要说的是,答案4是正确的答案:这似乎是一个有缺陷的设计。我无法证明这一点,但我想举一个例子。

当我实现边界体积层次结构时,我实现了与您的代码相似的东西。您可以将B视为树的分支类,并将A视为树的离开类(虽然不是完全正确,但应该给出一个主意)。每个分支都必须包含其叶子,因此B存储A的实例。但是从离开返回到分支也很不错,因此A应该存储对B的引用。您可以决定使边界体积层次结构存储任意内容(三角形,四边形,多边形),因此项目类型应为模板参数。同样,您可能决定将每个分支的最大请假数作为模板参数B

但是分支类为什么应该能够存储为任意分支创建的叶子(因为B类型是A的模板参数)?如果B存储着A的实例,那么我想不出任何理由使B类型为A的模板参数。

所以您在这里可能太笼统了,应该寻找其他设计。

期待您的评论。

答案 1 :(得分:1)

这是一种可能的方式。

// B doesn't know about A
template <class T1, class T2>
class B {};

// A doesn't know about B
template <template<class> class TB>
class A {
    using MyB = TB<A<TB>>;
};

template <class K>
    using Bint = B<K, int>;

int main() {
    A<Bint> abint;
}

编辑:如果B具有多个模板参数,则也可以这样做,尽管稍微复杂一点。人们需要使用一些助手将所有模板参数绑定到一个类中。这是一个例子。 (为了清楚起见,我将类重命名了。)

// The top class parameterised by 3 bottom classes
template <class A1, class A2, class A3>
struct Top {};

// Three bottom classes parameterised by the top
template <template<class> class T> struct Bottom1 {};
template <template<class> class T> struct Bottom2 {};
template <template<class> class T> struct Bottom3 {};

// Until this point, none of the classes know about any other.
// Now tie them together with these helper definitions.

template <typename K>
using BundledTop = Top<typename K::A1, typename K::A2, typename K::A3>;

struct Bundle
{
    using A1 = Bottom1<BundledTop>;
    using A2 = Bottom2<BundledTop>;
    using A3 = Bottom3<BundledTop>;
};

using MyTop = BundledTop<Bundle>;