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.

+3


source to share


2 answers


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.

+4


source


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"; 
}

      

+5


source







All Articles