Considere uma classe como esta (omitindo detalhes):
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) { /*...*/ }
// ...
};
Ao instanciar o modelo com um tipo de chave semelhante a uma string, gostaria que todas as funções-membro aceitassem apenas o string_view correspondente em vez de uma referência const ao tipo de chave real. Eu sei que posso obter isso com uma especialização parcial para cada classe de string possível:
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) { /*...*/ }
// ...
};
// ...
Os órgãos de todas as funções membro são exatamente os mesmos, a única alteração é a assinatura. Existe um mecanismo para expressar isso sem repetir todo o código?
Abordagem A
Você pode adicionar um alias à sua classe:
Sem mais alterações
my_flat_map
, você pode adicionar mais especializações parciaiskey_view
para lidar com mais tipos semelhantes a strings (talvez Qt ou boost strings).Notas Adicionais
Observe também que usar funções
key_view
ininsert_
não faz sentido. Ao inserir, a biblioteca padrão geralmente usa duas sobrecargas paraK&&
econst K&
. Você não pode inserir uma visualização de qualquer maneira, então mantenha-a:Para evitar repetições, você pode usar um modelo de função. Procure
std::map::try_emplace
inspiração no design:Abordagem B
Você pode simplesmente transformar algumas operações em modelos para suportar pesquisas heterogêneas de maneira geral, não apenas em casos especiais como
std::string_view
:Agora, se o usuário ligar
my_flat_map<std::string, ...>::find
comconst char*
,std::string
, oustd::string_view
, sempre funcionaria porquestd::string
tem uma==
operadora para estes.O usuário também pode fornecer um
Equal
objeto de função personalizado caso não haja nenhum arquivo==
.Nota: se você está se perguntando por que ambos
equality_with
eequality_with_impl
são necessários, consulte: Por que o conceito same_as verifica a igualdade de tipo duas vezes?Se você deseja mapear um tipo,
K
para outro, o argumento parafind
, você pode escrever uma característica e especializá-la:No entanto,
std::u16string
é apenas um alias,std::basic_string<char16_t>
assim comostd::u16string_view
é um alias parastd::basic_string_view<char16_t>
. Além disso,std::basic_string::CharT
é o tipo de caractere da string. Portanto, você pode salvar todos os padrões acima e usar o arquivostd::basic_string_view< typename K::CharT>
. E seother::esotic_string
não tiverCharT
(deveria), você pode recorrer ao acima que usaCharT
quando disponível e à sua especialização personalizada caso contrário.Você especializa a classe verificando se ela
basic_string_view
pode ser construída com o objeto do tipoconst K
:que abrange outros tipos semelhantes a strings, como
const char*
oustd::span<char8_t>
.