Consider the following example:
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>();
}
The Scanner
class is used to extract tokens from some source. The above code works fine, but fails when I try to get
other integral types like a char
or an unsigned int
. The code to read these types is exactly the same as the code to read an int
. I could just duplicate the code for all other integral types I'd like to read, but I'd rather define one function template for all integral types.
I've tried the following:
struct Scanner
{
template <typename T>
typename enable_if<boost::is_integral<T>, T>::type get();
};
Which works like a charm, but I am unsure how to get Scanner::get<string>()
to function again. So, how can I write code so that I can do scanner.get<string>()
and scanner.get<any integral type>()
and have a single definition to read all integral types?
Update: bonus question: What if I want to accept more than one range of classes based on some traits? For example: how should I approach this开发者_Go百科 problem if I want to have three get
functions that accept (i) integral types (ii) floating point types (iii) strings, respectively.
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";
}
};
Update "What if I want to accept more than one range of classes based on some traits?"
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");
}
};
Defer to another template. Here's the general pattern for what you want:
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>
{
};
精彩评论