Come posso scrivere una funzione di modello per tutti i tipi con un tipo particolare tratto?
-
22-09-2019 - |
Domanda
Si consideri il seguente esempio:
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>();
}
La classe Scanner
viene utilizzato per estrarre i token da qualche fonte. Il codice di cui sopra funziona bene, ma non riesce quando cerco di get
altri tipi integrali come un char
o un unsigned int
. Il codice per leggere questi tipi è esattamente lo stesso del codice per leggere un int
. Ho potuto solo duplicare il codice per tutti gli altri tipi integrali mi piacerebbe leggere, ma io preferisco definire una funzione di modello per tutti i tipi integrali.
Ho provato quanto segue:
struct Scanner
{
template <typename T>
typename enable_if<boost::is_integral<T>, T>::type get();
};
che funziona come un fascino, ma non sono sicuro come ottenere Scanner::get<string>()
a funzionare di nuovo. Così, come posso scrivere codice in modo che io possa fare scanner.get<string>()
e scanner.get<any integral type>()
e hanno una definizione unica di leggere tutti i tipi integrali?
Aggiornamento: question bonus : Cosa succede se voglio accettare più di una gamma di classi di base di alcuni tratti? Per esempio: come dovrei affrontare questo problema se voglio avere tre funzioni get
che accettare (i) tipi integrali (ii) Tipi di punto (iii) le stringhe galleggianti, rispettivamente,
Soluzione
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";
}
};
Aggiorna "E se voglio accettare più di una gamma di classi di base di alcuni tratti?"
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");
}
};
Altri suggerimenti
Rimanda ad un altro modello. Ecco il modello generale per ciò che si vuole:
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>
{
};