3

Currently I am overloading this function to generate a random number:

float GetRand(float lower, float upper) {                                                                                                                                                      
    std::random_device rd;                                                                                                                                                                    
    std::mt19937_64 mt(rd());                                                                                                                                                                 
    std::uniform_real_distribution<float> dist(lower,upper);                                                                                                                                  
    return dist(mt);                                                                                                                                                                            
}                                                                                                                                                                                              

int GetRand(int lower, int upper) {                                                                                                                                                            
    std::random_device rd;                                                                                                                                                                    
    std::mt19937_64 mt(rd());                                                                                                                                                                 
    std::uniform_int_distribution<int> dist(lower,upper);                                                                                                                                     
    return dist(mt);                                                                                                                                                                          
}                                                                                                                                                                                             

Is it possible to do this with a template? I don't know how I could template the distribution.

Alex Wicks
  • 55
  • 6

1 Answers1

9

We can unify both overloadings of GetRand as a function template.

First of all, please note that the effect of std::uniform_real_distribution<T> is undefined if T is not one of float, double and long double. For instance, 29.6.1.1 General requirements [rand.req.genl] in C++ standard draft n4687 states:

Throughout this subclause 29.6, the effect of instantiating a template:

...

d) that has a template type parameter named RealType is undefined unless the corresponding template argument is cv-unqualified and is one of float, double, or long double.

In addition, 29.6.8.2.2 Class template uniform_real_distribution [rand.dist.uni.real] describes std::uniform_real_distribution with the template type parameter RealType and therefore std::uniform_real_distribution<int> is undefined:

template<class RealType = double>
class uniform_real_distribution {
    ...
};

Also, similar restriction exists for std::uniform_int_distribution<T>. Thus we need to toggle the distribution type between std::uniform_real_distribution<T> and std::uniform_int_distribution<T> depending on T.


We can check the above restrictions using std::is_floating_point and std::is_integral and make the following switch:

#include <random>
#include <type_traits>

template<class T>
using uniform_distribution = 
typename std::conditional<
    std::is_floating_point<T>::value,
    std::uniform_real_distribution<T>,
    typename std::conditional<
        std::is_integral<T>::value,
        std::uniform_int_distribution<T>,
        void
    >::type
>::type;

Then two overloadings of GetRand can be unified to the following function template. Here I also avoid recursive construction of std::mt19937_64 and make the function thread-safe applying the accepted answer in this post.

template <class T>
T GetRand(T lower, T upper)
{
    static thread_local std::mt19937_64 mt(std::random_device{}());
    uniform_distribution<T> dist(lower,upper);

    return dist(mt);
}

Finally, the caller side would be as follows:

DEMO

auto i = GetRand<int>   (0, 1); // 0 or 1
auto f = GetRand<float> (0, 1); // [0, 1)
auto d = GetRand<double>(0, 1); // [0, 1)
Hiroki
  • 2,780
  • 3
  • 12
  • 26