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

condition checking on signatures of template of a class (Ex. HashSet + HashMap)

是否可以根据 class 模板中类型的某些条件来创建 return 类型的函数?

例子

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

它有一个适当的 begin() 和 end() 函数 return 迭代器。

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,太好了。

问题:

我也可以扩展这个 class 来替换 std::hashset 吗?如何?

具体来说,我想用 std::hashsetstd::unordered_map ] 直接。

动机:

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

难度:

问题是有很多次调用哈希集是这样的:-

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 
}

我也无法更改哈希映射的 return 签名,因为有些代码将其用作真实映射,而不是设置:-

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

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

我糟糕的解决方案:

创建一个MyHashSet来封装HashMap。它的缺点是我会多一层抽象,有两个classes,bug多,可维护性差

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

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

编译时可以使用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 是否是一个提高效率甚至可靠性的好主意(你可能不得不在几个地方进行这种调度),但如果它真的是什么您需要,这是一个相当干净的解决方案,它本身不会引入任何运行时开销。

请注意,映射/设置键在标准库中实际上是 const,因此来自间接运算符的 return 类型实际上应该是 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* 之前声明,因为它出现在函数体之外,在名称查找只能找到先前声明的地方(它不会查看整个 class 定义)。