在编译时选择随机数分布
Choose random number distribution at compile time
我正在使用 google 测试的 TYPED_TEST
功能编写测试,这使我可以将测试概括为多种类型。我正在测试类型 int
和 double
的 class 模板。在测试中,我需要生成 运行dom 数字。为此,我尝试使用 std::uniform_int_distribution<T>
和 std::uniform_real_distribution<T>
,但将 运行 放入静态断言中。
如名称所示,std::uniform_int_distribution<T>
checks if T
is an integral type and std::uniform_real_distribution<T>
检查 T
是否为浮点类型。
自从我的测试自动测试 int
然后 double
,我一直在尝试编写某种函数,使我能够为类型选择正确的分布编译时间。更准确地说,类似于:
template<class T>
Distribution get_right_distribution(const T& a, const T& b)
{
if(T is integral) // Compile time is needed, runtime
// fails since both if and else have to compile
{
return std::uniform_real_distribution(a, b);
}
else
{
return std::uniform_real_distribution(a, b);
}
}
请注意,这只是我一直在尝试做的伪代码。这种逻辑 b运行ch 失败,因为 if
和 else
必须编译。
我已经研究了如何做到这一点,我觉得 std::is_integral<T>
和 std::is_floating_point<T>
是解决方案的一部分,但到目前为止我还没有编译任何东西。我主要尝试了两件事:
- 使用模板专业化制作一种编译时间。
- 使用
enable_if
.
使用第一种方法,我得到了一条错误消息,告诉我我的重载不明确。使用第二种方法,我尝试了一些东西,但迷失在它导致的令人讨厌的语法中(至少对于不习惯它的人来说)。
对于如何实现这一点,您有什么建议吗?
P.S。我想看看如何做到这一点,所以将我的测试分成两部分对我来说不是一个可以接受的答案。
C++17
我可能会使用 C++17,你可以使用 if constexpr(...)
:
#include <iostream>
#include <random>
#include <type_traits>
template <typename T>
auto get_right_distribution(const T a, const T b) {
if constexpr(std::is_integral<T>::value) {
return std::uniform_int_distribution(a, b);
}
else {
return std::uniform_real_distribution(a, b);
}
}
int main() {
std::random_device rd;
std::mt19937 gen(rd());
auto int_dis = get_right_distribution(1, 6);
std::cout << int_dis(gen) << "\n";
auto float_dis = get_right_distribution(1.F, 6.F);
std::cout << float_dis(gen) << "\n";
}
C++11 & C++14
对于 C++11 和 C++14,您可以在模板参数列表中使用有条件的额外模板类型参数来 select return 类型以及分布。
C++11:
template <typename T,
typename Distribution = typename std::conditional<
std::is_integral<T>::value,
std::uniform_int_distribution<T>,
std::uniform_real_distribution<T>>::type>
Distribution get_right_distribution(const T a, const T b) {
return Distribution(a, b);
}
C++ 14(return 类型由 auto
推导并使用 std::conditional<...>::type
的 std::conditional_t
辅助类型缩写形式):
template <typename T,
typename Distribution = typename std::conditional_t<
std::is_integral<T>::value,
std::uniform_int_distribution<T>,
std::uniform_real_distribution<T>>>
auto get_right_distribution(const T a, const T b) {
return Distribution(a, b);
}
我有时会这样使用std::conditional:
template<typename Number>
Number random_number(Number from, Number to)
{
static_assert(std::is_integral<Number>::value
|| std::is_floating_point<Number>::value,
"parameters must be integer or floating point types");
using Distribution = typename std::conditional
<
std::is_integral<Number>::value,
std::uniform_int_distribution<Number>,
std::uniform_real_distribution<Number>
>::type;
// in reality I usually get the generator from another
// function, but for many purposes this is fine.
thread_local static std::mt19937 mt{std::random_device{}()};
thread_local static Distribution dist;
return dist(mt, typename Distribution::param_type{from, to});
}
如果您传递函数整数参数,它会选择 int
分布,否则它会选择 real
分布。
我正在使用 google 测试的 TYPED_TEST
功能编写测试,这使我可以将测试概括为多种类型。我正在测试类型 int
和 double
的 class 模板。在测试中,我需要生成 运行dom 数字。为此,我尝试使用 std::uniform_int_distribution<T>
和 std::uniform_real_distribution<T>
,但将 运行 放入静态断言中。
如名称所示,std::uniform_int_distribution<T>
checks if T
is an integral type and std::uniform_real_distribution<T>
检查 T
是否为浮点类型。
自从我的测试自动测试 int
然后 double
,我一直在尝试编写某种函数,使我能够为类型选择正确的分布编译时间。更准确地说,类似于:
template<class T>
Distribution get_right_distribution(const T& a, const T& b)
{
if(T is integral) // Compile time is needed, runtime
// fails since both if and else have to compile
{
return std::uniform_real_distribution(a, b);
}
else
{
return std::uniform_real_distribution(a, b);
}
}
请注意,这只是我一直在尝试做的伪代码。这种逻辑 b运行ch 失败,因为 if
和 else
必须编译。
我已经研究了如何做到这一点,我觉得 std::is_integral<T>
和 std::is_floating_point<T>
是解决方案的一部分,但到目前为止我还没有编译任何东西。我主要尝试了两件事:
- 使用模板专业化制作一种编译时间。
- 使用
enable_if
.
使用第一种方法,我得到了一条错误消息,告诉我我的重载不明确。使用第二种方法,我尝试了一些东西,但迷失在它导致的令人讨厌的语法中(至少对于不习惯它的人来说)。
对于如何实现这一点,您有什么建议吗?
P.S。我想看看如何做到这一点,所以将我的测试分成两部分对我来说不是一个可以接受的答案。
C++17
我可能会使用 C++17,你可以使用 if constexpr(...)
:
#include <iostream>
#include <random>
#include <type_traits>
template <typename T>
auto get_right_distribution(const T a, const T b) {
if constexpr(std::is_integral<T>::value) {
return std::uniform_int_distribution(a, b);
}
else {
return std::uniform_real_distribution(a, b);
}
}
int main() {
std::random_device rd;
std::mt19937 gen(rd());
auto int_dis = get_right_distribution(1, 6);
std::cout << int_dis(gen) << "\n";
auto float_dis = get_right_distribution(1.F, 6.F);
std::cout << float_dis(gen) << "\n";
}
C++11 & C++14
对于 C++11 和 C++14,您可以在模板参数列表中使用有条件的额外模板类型参数来 select return 类型以及分布。
C++11:
template <typename T,
typename Distribution = typename std::conditional<
std::is_integral<T>::value,
std::uniform_int_distribution<T>,
std::uniform_real_distribution<T>>::type>
Distribution get_right_distribution(const T a, const T b) {
return Distribution(a, b);
}
C++ 14(return 类型由 auto
推导并使用 std::conditional<...>::type
的 std::conditional_t
辅助类型缩写形式):
template <typename T,
typename Distribution = typename std::conditional_t<
std::is_integral<T>::value,
std::uniform_int_distribution<T>,
std::uniform_real_distribution<T>>>
auto get_right_distribution(const T a, const T b) {
return Distribution(a, b);
}
我有时会这样使用std::conditional:
template<typename Number>
Number random_number(Number from, Number to)
{
static_assert(std::is_integral<Number>::value
|| std::is_floating_point<Number>::value,
"parameters must be integer or floating point types");
using Distribution = typename std::conditional
<
std::is_integral<Number>::value,
std::uniform_int_distribution<Number>,
std::uniform_real_distribution<Number>
>::type;
// in reality I usually get the generator from another
// function, but for many purposes this is fine.
thread_local static std::mt19937 mt{std::random_device{}()};
thread_local static Distribution dist;
return dist(mt, typename Distribution::param_type{from, to});
}
如果您传递函数整数参数,它会选择 int
分布,否则它会选择 real
分布。