Different template behavior for integer and real types
How to create a template with different behavior for different types (some pseudo-language), for example:
template <typename T>
T GetRand(T param)
{
if (T is of any real type) {
// use uniform_real_distribution
}
if (T is of any integer type) {
// use uniform_int_distribution
}
if (T is of any other type) {
// do nothing
}
}
How do you write those if T is of xxx type
?
If it makes sense, I am using C ++ 11.
source to share
You can use SFINAE on every overload GetRand
, but at some point it becomes much easier to use tag posting. We're going to pick a unique type for each of our potential overloads (" T
is of any real type", " T
is of any integer type", "other") and builds the correct one as the second argument to the helper function.
First, we must select the correct tag:
struct integral_tag{};
struct floating_tag{};
struct other_tag{};
template <typename T>
struct get_tag
: std::conditional<
std::is_integral<T>::value,
integral_tag,
typename std::conditional<
std::is_floating_point<T>::value,
floating_tag,
other_tag>::type
>
{ };
template <typename T>
using get_tag_t = typename get_tag<T>::type;
We then use this type trait for our forwarding:
template <typename T>
T GetRand(T param) {
return GetRand(param, get_tag_t<T>{} );
}
And then just write these overloads:
template <typename T>
T GetRand(T param, integral_tag ) { ... };
template <typename T>
T GetRand(T param, floating_tag ) { ... };
template <typename T>
T GetRand(T param, other_tag ) { ... };
Personally, I find it easier to read.
source to share
The problem with using if statements like this is that bodies have to be compiled for anyone T
, whether or not that code ever gets executed.
You would be better off using a specialization or the like to define your behavior. An example that uses SFINAE and a header type_traits
:
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
T GetRand(T param)
{
cout << "int version\n";
}
template <typename T, std::enable_if_t<std::is_floating_point<T>::value>* = nullptr>
T GetRand(T param)
{
cout << "float version\n";
}
template <typename T,
std::enable_if_t<!(std::is_floating_point<T>::value ||
std::is_integral<T>::value)>* = nullptr>
T GetRand(T param)
{
cout << "other version\n";
}
source to share