Como posso escrever um modelo de função para todos os tipos com uma característica de tipo específico?
-
22-09-2019 - |
Pergunta
Considere o seguinte exemplo:
struct Scanner
{
template <typename T>
T get();
};
template <>
string Scanner::get()
{
return string("string");
}
template <>
int Scanner::get()
{
return 10;
}
int main()
{
Scanner scanner;
string s = scanner.get<string>();
int i = scanner.get<int>();
}
o Scanner
A classe é usada para extrair tokens de alguma fonte. O código acima funciona bem, mas falha quando tento get
outros tipos integrais, como um char
ou um unsigned int
. O código para ler esses tipos é exatamente o mesmo que o código para ler um int
. Eu poderia apenas duplicar o código para todos os outros tipos integrais que gostaria de ler, mas prefiro definir um modelo de função para todos os tipos integrais.
Eu tentei o seguinte:
struct Scanner
{
template <typename T>
typename enable_if<boost::is_integral<T>, T>::type get();
};
O que funciona como um charme, mas não tenho certeza de como conseguir Scanner::get<string>()
para funcionar novamente. Então, como posso escrever código para que eu possa fazer scanner.get<string>()
e scanner.get<any integral type>()
E tem uma única definição para ler todos os tipos integrais?
ATUALIZAÇÃO: Pergunta de bônus: E se eu quiser aceitar mais de uma variedade de classes com base em algumas características? Por exemplo: como devo abordar esse problema se quiser ter três get
funções que aceitam (i) tipos integrais (ii) tipos de pontos flutuantes (iii) strings, respectivamente.
Solução
struct Scanner
{
template <typename T>
typename boost::enable_if<boost::is_integral<T>, T>::type get()
{
return 10;
}
template <typename T>
typename boost::disable_if<boost::is_integral<T>, std::string>::type get()
{
return "string";
}
};
Atualize "E se eu quiser aceitar mais de uma variedade de classes com base em algumas características?"
struct Scanner
{
template <typename T>
typename boost::enable_if<boost::is_integral<T>, T>::type get()
{
return 10;
}
template <typename T>
typename boost::enable_if<boost::is_floating_point<T>, T>::type get()
{
return 11.5;
}
template <typename T>
std::string get(
typename boost::disable_if<boost::is_floating_point<T>, T>::type* = 0,
typename boost::disable_if<boost::is_integral<T>, T>::type* = 0)
{
return std::string("string");
}
};
Outras dicas
Adiar para outro modelo. Aqui está o padrão geral para o que você deseja:
template <typename T, bool HasTrait = false>
struct scanner_impl;
template <typename T>
struct scanner_impl
{
// Implement as though the trait is false
};
template <typename T>
struct scanner_impl<true>
{
// Implement as though the trait is true
};
// This is the one the user uses
template <typename T>
struct scanner : scanner_impl<T, typename has_my_trait<T>::value>
{
};