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).
CodePudding user response:
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
CodePudding user response:
According to cppreference, uniform_real_distribution
has such declaration:
template< class RealType = double >
class uniform_real_distribution;
// ctor
explicit uniform_real_distribution( RealType a, RealType b = 1.0 );
So when you used the form uniform_real_distribution<>
, it actually substitute the RealType
as double
(obtained from default template arguments). However, when you use uniform_real_distribution<T>
, you actually specific the RealType
as T
(in your sample case, which is int
), thus cause the some static_assert
check failure in the implementation of uniform_real_distribution
.