减少模板策略混乱

时间:2012-01-13 03:41:04

标签: c++ templates

如果我有以下模板参数:

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_RehashClassT_HashcodeClass是两个模板类,其中也包含T_Key, T_Value, T_HashFunc, T_ExtractKey and T_EqualKey。我希望这些类从Hashtable的typedef列表中获取类型(Hashtable中的所有模板参数类型都是typedefined)。

请注意,T_RehashClassT_HashcodeClass也可以由用户创建(提供默认值),如果用户愿意,还可以包含其他模板参数。

目前,任何T_RehashClass的类都必须填写T_Key, T_Value等模板参数,我将其视为代码重复。我希望该类以某种方式了解Hashtable,以便它可以通过创建自己的typedef来自动访问其typedef并自动推导T_Key, T_Value等类型。不幸的是,在这种情况下,我得到了循环依赖。

这种问题一般如何解决?

另请注意,我正在关注EASTL,其中EASTL使用多重继承来继承T_RehashClassT_HashnodeClass,因此,Hashtable具有更多模板参数。我想知道是否有办法解决它(即不继承两个策略并将它们作为模板参数,因为继承策略会降低灵活性)。


我想到的一个解决方案是拥有一个模板结构,其中包含从T_KeyT_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_RehashClassT_HashcodeClass可以采用默认值,这将消除代码重复。这种方法的问题在于用户使用起来更麻烦。

2 个答案:

答案 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_pkey_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”,因为它们是模板名称,而不是模板类型!