我有三个可以静态组合的类。最低级别的类是A
并且是具有单个参数的模板类。比如说,这可能是一个 int。然后我有一个更高级别的类,B
它取决于类型T
和A
。最后,我有一个更高级的类C
,它依赖T
于A
和B
。
我可以看到两种编码这些关系的方法,使用模板模板或公共范围的 typedef:
// template-template style:
template <typename T>
struct A {
T at_;
};
template <typename T,
template <typename> class A>
struct B {
A<T> ba_;
T bt_;
};
template <typename T,
template <typename> class A,
template <typename, template <typename> class> class B>
struct C {
B<T, A> cb_;
A<T> ca_;
T ct_;
};
// public-scoped typedef style:
template <typename T>
struct A2 {
typedef T TType;
T at_;
};
template <class A>
struct B2 {
typedef A AType;
AType ba_;
typename A::TType bt_;
};
template <class B>
struct C2 {
typedef B BType;
B cb_;
typename B::AType ca_;
typename B::AType::TType ct_;
};
// ...
// client code:
A<int> MyA;
B<int, A> MyB;
C<int, A, B> MyC;
A2<int> myA2;
B2<A2<int> > myB2;
C2<B2<A2<int> > > myC2;
现在考虑一个扩展模式的新类 D - 使用第一种方法,模板声明几乎完全无法管理:
// horrendous to define:
template <typename T,
template <typename> class A,
template <typename,
template <typename> class> class B,
template <typename,
template <typename> class,
template <typename, template <typename> class> class> class C>
struct D {
C<T, A, B> dc_;
B<T, A> db_;
A<T> da_;
T dt_;
};
// but nice to instantiate:
D<int, A, B, C> MyD;
第一种方法的优点是每个参与的班级不需要为更高级别的班级做准备。他们不需要意识到它们是层次结构的一部分,因此可以重用现有类而不更改它们。这在理论上感觉是可扩展的,但是如果不求助于可怕的预处理器宏,类声明语法很快就会变得几乎不可能处理。
或者:
template <class C>
struct D2 {
typedef C CType; // <-- not required right now, but better put it in just-in-case...
C dc_;
typename C::BType db_;
typename C::BType::AType da_;
typename C::BType::AType::TType ct_;
};
第二种方法更加简洁,但是它要求层次结构的参与者将他们的模板参数作为公共类型定义的类型提供。他们需要在这个编译时接口上进行合作,以便他们可以将较低级别的类型传递到更高级别。如果它们在以后成为此类层次结构的一部分,则需要进行广泛的更改。这似乎意味着最好将所有模板类中的所有模板参数作为公共范围的 typedef 提供,以防万一它们最终在这种层次结构中使用。这对我来说不是很有可扩展性,但我在标准库中看到过类似的东西,其中value_type
经常提供模板参数。
我的问题是 - 我错过了每种方法的其他优点/缺点,以及在创建一个不断增长的类库时,哪种方法可能最有效,这些类往往像这样静态组合?一种风格比另一种更广泛接受吗?这里有没有考虑的替代方案?
关于实际应用的说明 - 我已经有一个实际应用,其中涉及以某些方式表现的特殊类型的数值。这些值被包装在更高级别的类中,每个类都提供一层额外的功能——每层一个。例如,一层提供回调机制,另一层提供缓存,另一层提供值验证,另一层提供转换功能。较高层可能需要了解比直接下面的层更多的信息,因此需要了解用于组成较低类型的类型。每个级别都有几个不同的实现,因此需要指定这么多类型。该层次结构的各个部分可以在其他代码中使用,反过来该代码也可能发现自己也是层次结构的一部分。这是一个相当经典的“组合”