C++ - 模板化均匀分布?
C++ - Templated Uniform Distribution?
目前我正在重载这个函数来生成一个随机数:
float GetRand(float lower, float upper) {
std::random_device rd;
std::mt19937_64 mt(rd());
std::uniform_real_distribution<float> dist(lower,upper);
return dist(mt);
}
int GetRand(int lower, int upper) {
std::random_device rd;
std::mt19937_64 mt(rd());
std::uniform_int_distribution<int> dist(lower,upper);
return dist(mt);
}
可以用模板来做吗?我不知道如何为分发设置模板。
我们可以将 GetRand
的两个重载统一为一个函数模板。
首先请注意,如果T
不是float
、double
和long double
之一,则std::uniform_real_distribution<T>
的效果是不确定的。
例如,C++ 标准草案 n4687 中的 29.6.1.1 一般要求 [rand.req.genl] 指出:
Throughout this subclause 29.6, the effect of instantiating a template:
...
d) that has a template type parameter named RealType is undefined unless the corresponding template
argument is cv-unqualified and is one of float, double, or long double.
此外,29.6.8.2.2Class模板uniform_real_distribution[rand.dist.uni.real] 使用模板类型参数 RealType
描述 std::uniform_real_distribution
,因此 std::uniform_real_distribution<int>
未定义:
template<class RealType = double>
class uniform_real_distribution {
...
};
此外,std::uniform_int_distribution<T>
也存在类似的限制。
因此,我们需要根据 T
.
在 std::uniform_real_distribution<T>
和 std::uniform_int_distribution<T>
之间切换分布类型
我们可以使用 std::is_floating_point
和 std::is_integral
检查上述限制并进行以下切换:
#include <random>
#include <type_traits>
template<class T>
using uniform_distribution =
typename std::conditional<
std::is_floating_point<T>::value,
std::uniform_real_distribution<T>,
typename std::conditional<
std::is_integral<T>::value,
std::uniform_int_distribution<T>,
void
>::type
>::type;
那么GetRand
的两次重载可以统一到下面的函数模板中。
在这里,我还避免了 std::mt19937_64
的递归构造,并使函数线程安全地应用 this post.
中接受的答案
template <class T>
T GetRand(T lower, T upper)
{
static thread_local std::mt19937_64 mt(std::random_device{}());
uniform_distribution<T> dist(lower,upper);
return dist(mt);
}
最后调用方如下:
auto i = GetRand<int> (0, 1); // 0 or 1
auto f = GetRand<float> (0, 1); // [0, 1)
auto d = GetRand<double>(0, 1); // [0, 1)
目前我正在重载这个函数来生成一个随机数:
float GetRand(float lower, float upper) {
std::random_device rd;
std::mt19937_64 mt(rd());
std::uniform_real_distribution<float> dist(lower,upper);
return dist(mt);
}
int GetRand(int lower, int upper) {
std::random_device rd;
std::mt19937_64 mt(rd());
std::uniform_int_distribution<int> dist(lower,upper);
return dist(mt);
}
可以用模板来做吗?我不知道如何为分发设置模板。
我们可以将 GetRand
的两个重载统一为一个函数模板。
首先请注意,如果T
不是float
、double
和long double
之一,则std::uniform_real_distribution<T>
的效果是不确定的。
例如,C++ 标准草案 n4687 中的 29.6.1.1 一般要求 [rand.req.genl] 指出:
Throughout this subclause 29.6, the effect of instantiating a template:
...
d) that has a template type parameter named RealType is undefined unless the corresponding template argument is cv-unqualified and is one of float, double, or long double.
此外,29.6.8.2.2Class模板uniform_real_distribution[rand.dist.uni.real] 使用模板类型参数 RealType
描述 std::uniform_real_distribution
,因此 std::uniform_real_distribution<int>
未定义:
template<class RealType = double> class uniform_real_distribution { ... };
此外,std::uniform_int_distribution<T>
也存在类似的限制。
因此,我们需要根据 T
.
std::uniform_real_distribution<T>
和 std::uniform_int_distribution<T>
之间切换分布类型
我们可以使用 std::is_floating_point
和 std::is_integral
检查上述限制并进行以下切换:
#include <random>
#include <type_traits>
template<class T>
using uniform_distribution =
typename std::conditional<
std::is_floating_point<T>::value,
std::uniform_real_distribution<T>,
typename std::conditional<
std::is_integral<T>::value,
std::uniform_int_distribution<T>,
void
>::type
>::type;
那么GetRand
的两次重载可以统一到下面的函数模板中。
在这里,我还避免了 std::mt19937_64
的递归构造,并使函数线程安全地应用 this post.
template <class T>
T GetRand(T lower, T upper)
{
static thread_local std::mt19937_64 mt(std::random_device{}());
uniform_distribution<T> dist(lower,upper);
return dist(mt);
}
最后调用方如下:
auto i = GetRand<int> (0, 1); // 0 or 1
auto f = GetRand<float> (0, 1); // [0, 1)
auto d = GetRand<double>(0, 1); // [0, 1)