我有两个类,一个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>
,它是递归的。)
我期望的理想设计是A1
和A2
的程序员根本不需要彼此了解,而B
的程序员只需添加{{ 1}}和A1
差不多A2
(或添加一些其他内容),即使他添加了B<A1, A2>
A1 {{1} } A2`无需更改。
所以
1:如果我坚持使用这种语法,是否表示我必须放弃使用T3
的模板,而是使用指针?
2:如果我坚持使用模板,这是否意味着类B<A1, A2, A3>, the code of
和类and
必须彼此了解?
3:在B
和T1
独立的情况下,有没有第三种使用模板的方法?也就是说,无论我向类T2
添加了多少个参数,T1
类都不需要更改?模板模板/ CRTP / SFINAE,所有这些似乎都没有用。由于T2
类仅是类B
中的引用,因此A1
类更像是一个接口,而不是某个类,这使我想起了C ++提案“ concept”,可以在这种情况下有“概念”帮助吗?
4:这是xy问题吗?那意味着我不应该以这种方式设计我的代码模式吗?
答案 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>;