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



我创建了一个简单的模板函数来生成一个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<>默认为双倍,我不需要(。

对于积分类型,有std::uniform_int_distribution。您必须使用if constexpr或使用SFNIAE(具有类型特征(分别处理浮点和积分。顺便说一句,std::uniform_real_distribution:中有一个注释。如果不是float、double或long double之一,则效果是未定义的("这"涉及模板类型。(

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

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

coliru上的实时演示


受Jarod42s评论的启发,另一个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

coliru上的实时演示


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

对于在c++中模板化随机数生成器,在这个答案中提供了一个更好的替代方案。

根据cppreference,uniform_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(,从而导致在uniform_real_distribution的实现中某些static_assert检查失败。

最新更新