在编译时选择随机数分布

Choose random number distribution at compile time

我正在使用 google 测试的 TYPED_TEST 功能编写测试,这使我可以将测试概括为多种类型。我正在测试类型 intdouble 的 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 失败,因为 ifelse 必须编译。

我已经研究了如何做到这一点,我觉得 std::is_integral<T>std::is_floating_point<T> 是解决方案的一部分,但到目前为止我还没有编译任何东西。我主要尝试了两件事:

  1. 使用模板专业化制作一种编译时间。
  2. 使用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<...>::typestd::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 分布。