Consider a class like this (omitting details):
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) { /*...*/ }
// ...
};
When instantiating the template with a string-like key type I'd like to have all those member functions just accepting the corresponding string_view instead of a const reference to the actual key type. I know that I can obtain this with a partial specialization for each possible string class:
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) { /*...*/ }
// ...
};
// ...
The bodies of all the member functions are exactly the same, the only change is the signature. Is there a mechanism to express this without repeating all the code?
Approach A
You could add an alias to your class:
Without any further changes to
my_flat_map, you can add more partial specializations ofkey_viewto handle more string-like types (perhaps Qt or boost strings).Further Notes
Also note that using
key_viewininsert_functions makes no sense. When inserting, the standard library usually uses two overloads forK&&andconst K&. You can't insert a view anyway, so keep it:To avoid repetition, you can use a function template instead. Look into
std::map::try_emplacefor design inspiration:Approach B
You can simply turn some operations into templates to generally support heterogeneous lookup, not just in special cases like
std::string_view:Now, if the user calls
my_flat_map<std::string, ...>::findwith aconst char*,std::string, orstd::string_view, it would always work becausestd::stringhas a==operator for these.The user can also provide a custom
Equalfunction object in case there is no appropriate==.Note: if you're wondering why both
equality_withandequality_with_implare necessary, refer to: Why does same_as concept check type equality twice?