Reputation: 8401
How to create 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 to write those if T is of xxx type
?
If it makes sense, I use C++11.
Upvotes: 3
Views: 157
Reputation: 303377
You can use SFINAE on every overload of GetRand
, but at some point it becomes a lot simpler to use tag-dispatch. 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 construct the correct one as a second argument to a helper function.
First, we have to pick 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;
Then, we use that type trait to do our forwarding:
template <typename T>
T GetRand(T param) {
return GetRand(param, get_tag_t<T>{} );
}
And then just write those 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 think that's easier to read.
Upvotes: 4
Reputation: 65640
The problem with using if statements like that is that the bodies need to be compilable for any T
, regardless of whether that code would ever actually be run.
You would be better off using specialization or similar to specify your behaviour. An example which uses SFINAE and the type_traits
header:
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";
}
Upvotes: 5