I created a simple template function to generate a random number of type T (I need int or float) in range [low, high) as follows:
template <typename T>
T randm(T low, T high)
{
static std::random_device seeder;
static std::mt19937 gen(seeder());
std::uniform_real_distribution<T> dis(low, high);
return dis(gen);
}
However when I try to call it as:
int r = randm<int>(0, 10);
I get the error: "static assertion failed: result_type must be a floating point type".
I found out that if I use uniform_real_distribution<> instead of uniform_real_distribution<T>, it works, but I am not sure why (and if I am not mistaken uniform_real_distribution<> defaults to double which I don’t need).
>Solution :
For integral types, there is std::uniform_int_distribution. You have to apply something with if constexpr or to use SFNIAE (with type traits) to handle floating points and integrals separately. Btw. there is a note in std::uniform_real_distribution: The effect is undefined if this is not one of float, double, or long double. ("this" concerns the template type.)
Two separate functions distinguished by SFINAE:
#include <iostream>
#include <random>
template <typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0
>
T randm(T low, T high)
{
static std::random_device seeder;
static std::mt19937 gen(seeder());
std::uniform_int_distribution<T> dis(low, high);
return dis(gen);
}
template <typename T,
std::enable_if_t<std::is_floating_point_v<T>, int> = 0
>
T randm(T low, T high)
{
static std::random_device seeder;
static std::mt19937 gen(seeder());
std::uniform_real_distribution<T> dis(low, high);
return dis(gen);
}
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
DEBUG(std::cout << randm(0, 10) << std::endl);
DEBUG(std::cout << randm(0.0f, 10.0f) << std::endl);
}
Output:
std::cout << randm(0, 10) << std::endl;
4
std::cout << randm(0.0f, 10.0f) << std::endl;
9.05245
Using if constexpr (requires at least C++17):
#include <iostream>
#include <random>
template <typename T>
T randm(T low, T high)
{
static std::random_device seeder;
static std::mt19937 gen(seeder());
if constexpr (std::is_integral_v<T>) {
std::uniform_int_distribution<T> dis(low, high);
return dis(gen);
}
if constexpr (std::is_floating_point_v<T>) {
std::uniform_real_distribution<T> dis(low, high);
return dis(gen);
}
return T(); // ERROR?
}
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
DEBUG(std::cout << randm(0, 10) << std::endl);
DEBUG(std::cout << randm(0.0f, 10.0f) << std::endl);
}
Output:
std::cout << randm(0, 10) << std::endl;
7
std::cout << randm(0.0f, 10.0f) << std::endl;
3.51174