【问题标题】:Template function for random number generation (static assertion failed)随机数生成的模板函数(静态断言失败)
【发布时间】:2021-11-18 10:17:01
【问题描述】:

我创建了一个简单的模板函数,用于在 [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&lt;&gt; 而不是uniform_real_distribution&lt;T&gt;,它可以工作,但我不知道为什么(如果我没记错的话uniform_real_distribution&lt;&gt; 默认为我不需要的两倍)。

【问题讨论】:

  • 对于整数类型,有uniform_int_distribution。您必须使用 if constexpr 或使用 SFNIAE(具有类型特征)分别处理浮点和积分。顺便提一句。 std::uniform_real_distribution 中有一条注释:如果 this 不是 float、double 或 long double 之一,则效果未定义。(“this”涉及模板类型。)
  • 所以我想写两个单独的函数更容易,一个用于浮点数,一个用于整数。
  • 两个独立的函数,一个用于浮点数,一个用于整数 这是一种选择。即使在这种情况下,您也可以使用 SFINAE 方法,因为有不止一种浮点类型以及不止一种整数类型。

标签: c++ templates random static


【解决方案1】:

对于整数类型,有std::uniform_int_distribution。您必须使用 if constexpr 或使用 SFNIAE(具有类型特征)分别处理浮点和积分。顺便提一句。 std::uniform_real_distribution 中有一条注释:如果 this 不是 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


灵感来自 Jarod42s comment 另一个 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


Jarod42 还指出了这种方法的另一个弱点:std::is_integral 涵盖了任何整数类型(包括 char 甚至 bool 的变体),但如果模板类型 是,std::uniform_int_distribution 实际上是未定义的不是 short、int、long、long long、unsigned short、unsigned int、unsigned long 或 unsigned long long 之一。

answer to templating a random number generator in c++ 提供了更好的替代方案。

【讨论】:

  • 类似std::conditional_t&lt;std::is_integral&lt;T&gt;, std::uniform_int_distribution&lt;T&gt;, std::uniform_real_distribution&lt;T&gt;&gt; 是另一种选择。
  • @Jarod42 酷。我还不知道std::conditional(以及它的好处)......
  • 请注意,正如我们在链接答案中看到的那样,实际条件是幼稚的,std::uniform_int_distribution&lt;char&gt; 是错误的,即使 char 是整数。
  • @Jarod42 运气好的话,尝试使用boolchar 的方法可能会以static_assert() 结束,正如OP 最初为std::uniform_real_distribution 报告的那样。 ;-)
【解决方案2】:

根据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&lt;&gt; 形式时,它实际上将RealType 替换为double(从默认模板参数获得)。但是,当您使用uniform_real_distribution&lt;T&gt; 时,实际上将RealType 指定为T(在您的示例中为int),从而导致uniform_real_distribution 的实现中的某些static_assert 检查失败。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-25
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多