我创建了一个简单的模板函数来生成一个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
检查失败。