2012-01-13 71 views
2

如果我有下面的模板参数:减少模板策略杂波

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中的所有模板参数类型均为类型定义)。

请注意,T_RehashClassT_HashcodeClass也可以由用户创建(默认提供),如果用户希望可以有其他模板参数。

现在,任何类T_RehashClass必须有T_Key, T_Value等模板参数填写,我认为这是代码重复。我希望该课程知道Hashtable,以便它可以通过创建自己的typedef自动访问其typedefs并自动推断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可以再取这将消除代码重复的默认值。这种方法的问题是用户使用起来更麻烦。

+0

我很好奇你为什么需要这么多的灵活性,允许用户指定表会变得如何改头换面......这真的好像YAGNI应该在踢 - 你想解决什么问题需要这么多的定制? – Yuushi 2012-01-13 03:47:02

+0

'T_ExtractKey'和'T_EqualKey'是多余的(你可以提取关键的平等的测试,没有必要为一个独立的仿函数的一部分),以及'T_HashFunc'和'T_HashcodeClass'似乎也。 – 2012-01-13 03:48:40

+0

@CatPlusPlus:那么,在这个实现,但它有节点存储hashkey与否的选项。 'T_HashcodeClass'有两个不同的实现,一个假设hashkey存储,另一个不存在。至于其他人,我也不完全确定,但EASTL拥有它。现在我知道这是不是包括它的理由,但我想笔者知道的比我多,我最终会明白(以下简称“最终understand'部分做了其他容器发生在我身上,而下面EASTL) – Samaursa 2012-01-13 03:55:21

回答

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的前面,因为他们是模板的名称,而不是模板类型!

+0

感谢。这实际上与EASTL正在做的非常接近。我读现代C++设计将重新您指出我同意的配置有趣的是只有谁写它的人的部分(+1) – Samaursa 2012-01-18 02:14:50

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是无效的,除非它具有相同的KeyPolicy作为rehast等,所以不要给它一个。

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 

显然你可以添加你想要的任何typedefs。如果我在代码审查是一个坚持己见的人我可能会问的东西像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; 
} 

很多细节未填写很明显,但你的想法。

+0

。因为我正在编写自己的Hashtable,所以我对它也很感兴趣:)但是我同意,它增加了最终用户的混乱,并可能使他们感到困惑(可能是我在路上)。参考任何未来的SOer,在上述实现中,除非使用基类优化,否则“细节”类将占用额外的空间。 +1进行详细解释。 – Samaursa 2012-01-18 02:39:49

+0

@Samaursa最终用户不必详细处理任何事情,除非他们想要。您可以将其隐藏在完全不同的标题中,以便最终用户不必知道。基类是“优化”任何东西,它增加了额外的虚拟呼叫。如果这对你更有意义,那么维护起来就更简单了。我会说这很有价值。 – 2012-01-18 15:32:06

+0

在这种情况下,'Hashtable'是用户不会直接使用的东西。他们将使用一个关联容器,比如'hash_map',它将使用'Hashtable'作为其逻辑。所以基本上,所有这些灵活性都是为了图书馆作者(现在我)。所以是的,我将主要“隐藏”细节,并像你指出的那样公开一个'hash_map'类。至于基类优化,我没有仔细阅读。你正在声明'typedef's,我认为那里有一个变量。 – Samaursa 2012-01-18 16:07:06