Home > Blockchain >  Template function for random number generation (static assertion failed)
Template function for random number generation (static assertion failed)

Time:11-18

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

Live Demo on coliru

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

Live Demo on coliru

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.

  • Related