随机数生成模板函数(静态断言失败)
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我不需要)。
根据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
),从而导致一些 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
使用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
受 另一个 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
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.
.
中提供了更好的选择
我创建了一个简单的模板函数来生成 [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我不需要)。
根据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
),从而导致一些 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
使用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
受
#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
char
and even bool
) but std::uniform_int_distribution 如果模板类型 不是 short、int、long、long long、unsigned short、unsigned int、unsigned long 或 unsigned long long.