简体   繁体   English

在编译时选择随机数分布

[英]Choose random number distribution at compile time

I am writing tests using the TYPED_TEST feature of google tests, which allows me to generalize a test to multiple types. 我正在使用google测试的TYPED_TEST功能编写测试,这允许我将测试推广到多种类型。 I am testing a class template for the types int and double . 我正在测试类型intdouble的类模板。 In a test, I would need to generate random numbers. 在测试中,我需要生成随机数。 To do so, I have tried using the std::uniform_int_distribution<T> and the std::uniform_real_distribution<T> but have ran into static asserts. 为此,我尝试使用std::uniform_int_distribution<T>std::uniform_real_distribution<T>但已经遇到静态断言。

As the names indicate, std::uniform_int_distribution<T> checks if T is an integral type and std::uniform_real_distribution<T> checks that T is a floating point type. 如名称所示, std::uniform_int_distribution<T>检查T是否为整数类型, std::uniform_real_distribution<T>检查T是否为浮点类型。

Since my test automatically tests for int and then for double , I have been trying to write some kind of function that would allow me to chose the right kind of distribution for the type at compile time. 由于我的测试自动测试int然后为double ,我一直在尝试编写某种函数,这将允许我在编译时为类型选择正确的类型。 More precisely, something like: 更确切地说,类似于:

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);
    }
}

Note that this is only a pseudocode of what I have been trying to do. 请注意,这只是我尝试做的伪代码。 This kind of logical branch fails because the if AND the else have to compile. 这种逻辑分支失败,因为ifelse必须编译。

I have done some research on how to do this and I feel like std::is_integral<T> and std::is_floating_point<T> are part of the solution, but I have not been able to compile anything so far. 我已经做了一些研究如何做到这一点,我觉得std::is_integral<T>std::is_floating_point<T>是解决方案的一部分,但到目前为止我还没能编译任何东西。 I mainly tried two things: 我主要尝试了两件事:

  1. Make a kind of compilation time by using template specialization. 使用模板专业化创建一种编译时间。
  2. Use enable_if . 使用enable_if

Using the first approach I ended up with an error message telling me my overloads were ambiguous. 使用第一种方法,我最终得到一条错误消息,告诉我我的重载是模棱两可的。 Using the second approach, I tried some stuff but got lost in the abominable syntax (at least for someone not used to it) which it lead to. 使用第二种方法,我尝试了一些东西,但却迷失了恶劣的语法(至少对于那些不习惯它的人来说)。

Do you have a suggestion on how this could be accomplished? 你有关于如何实现这一目标的建议吗?

PS I would like to see how this could be done, so splitting my test in two would not be an acceptable answer for me. PS我想知道如何做到这一点,所以将我的测试分成两部分对我来说不是一个可接受的答案。

C++17 C ++ 17

I you may use C++17, you can make use of if constexpr(...) : 我可以使用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

For C++11 and C++14, you could use a conditional extra template type parameter in your template parameter list to select the return type as well as the distribution. 对于C ++ 11和C ++ 14,您可以在模板参数列表中使用条件额外模板类型参数来选择返回类型和分布。

C++11: 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 type deduced by auto and using the std::conditional_t helper type short form for std::conditional<...>::type ): C ++ 14(由auto推导的返回类型,并使用std::conditional<...>::typestd::conditional_t helper类型的短格式):

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);
}

I sometimes use std::conditional like this: 我有时会像这样使用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});
}

If you pass the function integer parameters it selects the int distribution otherwise it selects the real distribution. 如果传递函数整数参数,则选择int分布,否则选择real分布。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM