考虑这样一个类(省略细节):
template<typename K, typename V>
class my_flat_map
{
// A lot of member functions taking a const ref to key
auto find(K const& key) { /*...*/ }
auto contains(K const& key) { /*...*/ }
auto insert_or_assign(K const& key, V const& val) { /*...*/ }
auto insert_if_missing(K const& key, V const& val) { /*...*/ }
// ...
};
当使用类似字符串的键类型实例化模板时,我希望所有这些成员函数只接受相应的 string_view 而不是对实际键类型的 const 引用。我知道我可以通过对每个可能的字符串类进行部分专业化来获得它:
template<typename V>
class my_flat_map<std::u32string,V>
{
auto find(std::u32string_view key) { /*...*/ }
// ...
};
template<typename V>
class my_flat_map<std::u16string,V>
{
auto find(std::u16string_view key) { /*...*/ }
// ...
};
template<typename V>
class my_flat_map<std::u8string,V>
{
auto find(std::u8string_view key) { /*...*/ }
// ...
};
template<typename V>
class my_flat_map<other::esotic_string,V>
{
auto find(other::esotic_string_view key) { /*...*/ }
// ...
};
// ...
所有成员函数的主体完全相同,唯一的变化是签名。是否有一种机制可以在不重复所有代码的情况下表达这一点?
方法A
您可以为您的类添加别名:
无需对 进行任何进一步的更改
my_flat_map
,您可以添加更多的部分特化key_view
来处理更多类似字符串的类型(可能是 Qt 或 boost 字符串)。进一步说明
另请注意,
key_view
在insert_
函数中使用是没有意义的。插入时,标准库通常使用K&&
和的两个重载const K&
。无论如何你都无法插入视图,所以保留它:为了避免重复,您可以使用函数模板。寻找
std::map::try_emplace
设计灵感:方法B
您可以简单地将一些操作转换为模板来普遍支持异构查找,而不仅仅是在特殊情况下,例如
std::string_view
:现在,如果用户
my_flat_map<std::string, ...>::find
使用const char*
、std::string
或进行呼叫std::string_view
,它总是可以工作,因为std::string
有一个==
操作符可以处理这些操作。Equal
如果没有合适的,用户还可以提供自定义函数对象==
。注意:如果您想知道为什么
equality_with
和equality_with_impl
都是必要的,请参阅:为什么 same_as 概念检查类型相等性两次?如果您想将一种类型 映射
K
到另一种类型(参数 )find
,您可以编写一个特征并将其特殊化:然而,
std::u16string
只是 的别名,std::basic_string<char16_t>
就像std::u16string_view
的别名一样std::basic_string_view<char16_t>
。此外,std::basic_string::CharT
是字符串的字符类型。因此,您可以保存上述所有样板并使用std::basic_string_view< typename K::CharT>
. 如果other::esotic_string
没有CharT
(应该),您可以回退到上面CharT
可用时使用的内容,否则您可以使用自定义专业化。basic_string_view
您可以通过检查是否可以使用类型的对象来构造类来专门化该类const K
:它涵盖了其他类似字符串的类型,例如
const char*
orstd::span<char8_t>
。