随机数生成模板函数(静态断言失败)

Template function for random number generation (static assertion failed)

我创建了一个简单的模板函数来生成 [low, high) 范围内的 T 类型随机数(我需要 int 或 float),如下所示:

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);
}

然而,当我尝试将其命名为:

int r = randm<int>(0, 10);

我收到错误:“静态断言失败:result_type 必须是浮点类型”。

我发现如果我使用 uniform_real_distribution<> 而不是 uniform_real_distribution<T>,它会起作用,但我不确定为什么(如果我没记错的话 uniform_real_distribution<> 默认为 double我不需要)。

根据cppreferenceuniform_real_distribution有这样的声明:

template< class RealType = double >
class uniform_real_distribution;

// ctor
explicit uniform_real_distribution( RealType a, RealType b = 1.0 );

因此,当您使用 uniform_real_distribution<> 形式时,它实际上将 RealType 替换为 double(从默认模板参数获得)。但是,当您使用 uniform_real_distribution<T> 时,您实际上将 RealType 指定为 T(在您的示例中为 int),从而导致一些 static_assert检查 uniform_real_distribution.

执行失败

对于整数类型,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如果这不是 float、double 或 long double 之一,则效果未定义。(“this”涉及模板类型。)

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);
}

输出:

std::cout << randm(0, 10) << std::endl;
4
std::cout << randm(0.0f, 10.0f) << std::endl;
9.05245

Live Demo on coliru


使用if constexpr(至少需要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);
}

输出:

std::cout << randm(0, 10) << std::endl;
7
std::cout << randm(0.0f, 10.0f) << std::endl;
3.51174

Live Demo on coliru


另一个 C++11 解决方案的启发:

#include <iostream>
#include <random>
#include <type_traits>

template <typename T>
T randm(T low, T high)
{
  static std::random_device seeder;
  static std::mt19937 gen(seeder());
  typename std::conditional<std::is_integral<T>::value,
    std::uniform_int_distribution<T>,
    std::uniform_real_distribution<T>
  >::type 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);
}

输出:

std::cout << randm(0, 10) << std::endl;
2
std::cout << randm(0.0f, 10.0f) << std::endl;
4.36778

Live Demo on coliru


also pointed out another weakness of this approach: std::is_integral covers any integral type (including variations of char and even bool) but std::uniform_int_distribution 如果模板类型 不是 short、int、long、long long、unsigned short、unsigned int、unsigned long 或 unsigned long long.

.

中提供了更好的选择