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::hashset 和 std::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
}
注意:有些行(#)不要更改。原因是:-
它们出现在很多地方。
更改它们也会使代码变脏。
将来我可能想将 MyHashMap/MyHashSet 替换回 std:: unordered_map/std::hashset 稍后。
如果我不修改#-line,改回来几乎不需要做任何工作(高模块化和可维护性)。
我糟糕的解决方案:
创建一个MyHashSet来封装HashMap。它的缺点是我会多一层抽象,有两个classes,bug多,可维护性差
我希望有一个技巧可以利用/操纵模板来检测 T 是 MyHashMap_DUMMY。
也就是说,
- 如果 T == MyHashMap_DUMMY,iterator->operator*() 将 return K&
- 否则 iterator->operator*() 将 return std::pair.
允许使用 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 定义)。
是否可以根据 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::hashset 和 std::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
}
注意:有些行(#)不要更改。原因是:-
它们出现在很多地方。
更改它们也会使代码变脏。
将来我可能想将 MyHashMap/MyHashSet 替换回 std:: unordered_map/std::hashset 稍后。
如果我不修改#-line,改回来几乎不需要做任何工作(高模块化和可维护性)。
我糟糕的解决方案:
创建一个MyHashSet来封装HashMap。它的缺点是我会多一层抽象,有两个classes,bug多,可维护性差
我希望有一个技巧可以利用/操纵模板来检测 T 是 MyHashMap_DUMMY。
也就是说,
- 如果 T == MyHashMap_DUMMY,iterator->operator*() 将 return K&
- 否则 iterator->operator*() 将 return std::pair.
允许使用 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 定义)。