条件检查类模板的签名(例如HashSet + HashMap)

时间:2016-06-07 14:15:54

标签: c++ templates maintainability

是否可以使函数的返回类型取决于类的模板中类型的某些条件?

实施例

我有一个自定义哈希映射,名为 MyHashMap

它有一个正确的begin()和end()函数,它返回迭代器。

template <class K, class T> class MyHashMap{
    MyIterator<K,T> begin() { ..... }
    MyIterator<K,T> end()  { ...... }
    //.... some function ....
}

迭代器有一个简洁的运算符*(),运算符++()等

template <class K, class T> class MyIterator{
    std::pair<K,T> operator*(){ ..... }
    //.... some function ....
}

现在我可以使用 MyHashMap 代替 std :: unordered_map ,非常好。

问题:

我是否可以扩展此类以替换 std :: hashset ?怎么样?

具体来说,我想直接用此类替换 std :: hashset std :: unordered_map

动机:

两种数据结构(map&amp; hashset)似乎非常相似(代码和逻辑),将它们放在一个地方可以提高可维护性。

难度:

问题是有几百次调用Hash设置如下: -

std::hashset<X> hashset; //old code, will be deleted
MyHashMap<X, MyHashMap_DUMMY > hashset; // new code
for(auto x: hashset ){ 
    x.doSomething(); //# old code, compile error, but I don't want to change this 
}

我无法更改哈希映射的返回签名,因为有些代码将其用作实际地图,而不是设置: -

for(auto xy: hashmap ){ //HashMap<X,Y>
    x.first.doSomething(); //# I don't want to change this line too
}   

注意:不应更改某些行(#)。原因是: -

  • 他们出现在很多地方。

  • 更改它们也会使代码变脏。

  • 将来我可能想将 MyHashMap / MyHashSet 替换回 std :: unordered_map / std :: hashset 以后。

  • 如果我不修改#-line,几乎没有什么工作可以改变(高模块性和可维护性)。

我的解决方案很差:

创建 MyHashSet 以封装 HashMap 。它的缺点是我将有另一层抽象,有两个类,更多的错误和更少的可维护性。

我希望有一个技巧可以利用/操纵模板来检测T是MyHashMap_DUMMY。
换句话说,

  • 如果T == MyHashMap_DUMMY,则iterator-&gt;运算符*()将返回K&amp;
  • 否则iterator-&gt;运算符*()将返回std :: pair。

允许使用C ++ 11和C ++ 14.

1 个答案:

答案 0 :(得分:1)

您可以使用tag dispatching在编译时启用T

template <class K, class T> struct MyIterator
{
   decltype(auto) operator*() 
   {
      return indirection_hlp(std::is_same<T, MyHashMap_DUMMY>{});
   }

   std::pair<K, T>& indirection_hlp(std::false_type)
   {
      // ...
   }

   K& indirection_hlp(std::true_type)
   {
      // ...
   }

   // other stuff
};

如上所述,这需要C ++ 14(因为decltype(auto) operator*()),但是同样的事情可以在C ++ 11中实现,只需要输入更多内容。

我不确定在效率甚至可靠性方面重用MyHashMap是否是一个好主意(你可能需要在几个地方进行这种调度),但如果它真的是你需要的话,这是一个相当干净的解决方案,它本身不会引入任何运行时开销。

请注意,map / set键在标准库中实际上是const,因此间接运算符的返回类型应该是std::pair<const K, T>&const K&

另请注意,for循环使用auto x,这将推断出非引用类型,因此会制作副本,这可能是您想要的一个集合,但可能不适用于地图(它将复制std::pair)。

对于C ++ 11,它看起来像这样:

template <class K, class T> struct MyIterator
{
   std::pair<K, T>& indirection_hlp(std::false_type)
   {
      // ...
   }

   K& indirection_hlp(std::true_type)
   {
      // ...
   }

   auto operator*() -> decltype(indirection_hlp(std::is_same<T, MyHashMap_DUMMY>{}))
   {
      return indirection_hlp(std::is_same<T, MyHashMap_DUMMY>{});
   }

   // other stuff
};

注意indirection_hlp在这种情况下需要在operator*之前声明,因为它出现在函数体之外,在名称查找只找到先前声明的地方(它没有查看)全班定义)。