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.
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:
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)