如果我有以下模板参数:
template <typename T_Key, typename T_Value, typename T_HashFunc,
typename T_ExtractKey, typename T_EqualKey, typename T_RehashClass, typename T_HashcodeClass>
class Hashtable { /* ... */ };
T_RehashClass
和T_HashcodeClass
是两个模板类,其中也包含T_Key, T_Value, T_HashFunc, T_ExtractKey and T_EqualKey
。我希望这些类从Hashtable
的typedef列表中获取类型(Hashtable
中的所有模板参数类型都是typedefined)。
请注意,T_RehashClass
和T_HashcodeClass
也可以由用户创建(提供默认值),如果用户愿意,还可以包含其他模板参数。
目前,任何T_RehashClass
的类都必须填写T_Key, T_Value
等模板参数,我将其视为代码重复。我希望该类以某种方式了解Hashtable
,以便它可以通过创建自己的typedef来自动访问其typedef并自动推导T_Key, T_Value
等类型。不幸的是,在这种情况下,我得到了循环依赖。
这种问题一般如何解决?
另请注意,我正在关注EASTL,其中EASTL使用多重继承来继承T_RehashClass
和T_HashnodeClass
,因此,Hashtable
具有更多模板参数。我想知道是否有办法解决它(即不继承两个策略并将它们作为模板参数,因为继承策略会降低灵活性)。
我想到的一个解决方案是拥有一个模板结构,其中包含从T_Key
到T_EqualKey
的所有模板参数。 Hashtable
声明将是:
template <typename T_HashtableParams, typename T_RehashClass = default_rehash_class<T_HashtableParams>, typename T_HashcodeClass = default_hashnode_class<T_HashtableParams> >
class Hashtable { /* ... */ };
T_RehashClass
和T_HashcodeClass
可以采用默认值,这将消除代码重复。这种方法的问题在于用户使用起来更麻烦。
答案 0 :(得分:2)
我不确定为hash和rehash类指定不同的T_Key和T_Value类型是非常有趣的。如果我正在解决这个问题,我会先尝试为key / val设置一个策略。我倾向于说可能应该有一个ValuePolicy而不是用KeyPolicy对它进行分组,但这既不在这里也不在那里。
namespace hash {
namespace detail {
template<
typename Key
, typename Value
, typename KeyGetter
, typename KeyComparator>
class KeyPolicy {
public:
typedef Key key_t;
typedef Value value_t;
typedef KeyGetter get_t;
typedef KeyComparator compare_t;
};
}} // hash detail
HashTable
无效,除非它与rehast具有相同的KeyPolicy等,所以不要给它一个。
namespace hash {
namespace detail {
template<typename RehashPolicy>
class HashTableImpl {
public:
typedef RehashPolicy rehash_p;
typedef typename rehash_p::key_policy_t::key_t key_t;
// typedef ...
};
// this doesn't have a specific name, its anything.
template<typename KeyPolicy>
class SomeRehashPolicy {
public:
typedef KeyPolicy key_policy_t;
};
}} // hash detail
显然,你可以添加你想要的任何typedef。如果我在代码审查中遇到困难,我可能会要求rehash_p
和key_policy_t
之类的内容是私有的。实际上,它们是实现细节。您试图保护的实际不变量是key_t
等等。
也许我超出礼仪的合理范围,但我的诚实意见是所有这些配置只对编写它的人有趣。不是你,不是任何人使用它。所以我只公开人们实际要使用的HashTable配置。
namespace hash {
struct stub {}; // sorry, I'm lazy
template<typename Key, typename Value>
class HashTable {
private:
typedef typename detail::KeyPolicy<Key, Value, stub, stub> key_p;
typedef typename detail::SomeRehashPolicy<key_p> rehash_p;
typedef typename detail::HashTableImpl<rehash_p> impl_t;
public:
typedef typename impl_t::key_t key_t;
};
} // hash
int main(int argc, char ** argv) {
hash::HashTable<int, double> hash_table;
return 0;
}
很明显没有填写很多细节,但是你明白了。
答案 1 :(得分:1)
参见例如现代C ++设计(第1.5.1节:使用模板模板参数实现策略)。我们的想法是让T_RehashClass和T_HashcodeClass成为模板模板参数。这些类将T_Key,T_Value和其他任何内容作为自己的参数。为避免重新输入,您可以继承这些模板所需的实例化。
template <
typename T_Key,
typename T_Value,
typename T_HashFunc,
typename T_ExtractKey,
typename T_EqualKey,
template<typename, typename /*, more params */> class T_RehashClass,
template<typename, typename /*, more params */> class T_HashcodeClass
> class Hashtable
:
public T_RehashClass<T_Key, T_Value /*, more params */>,
public T_HashcodeClass<T_Key, T_Value /*, more params */>
{ /* ... */ };
注意:你真的需要在T_RehashClass和T_HashcodeClass前面使用“class”而不是“typename”,因为它们是模板名称,而不是模板类型!