С++ - шаблонное равномерное распределение?

В настоящее время я перегружаю эту функцию для генерации случайного числа:

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

Можно ли это сделать с помощью шаблона? Я не знаю, как я мог создать шаблон для дистрибутива.


person Alex Wicks    schedule 21.03.2019    source источник


Ответы (1)


Мы можем объединить обе перегрузки GetRand в качестве шаблона функции.

Прежде всего, обратите внимание, что эффект std::uniform_real_distribution<T> не определено, если T не является одним из float, double и long double. Например, 29.6.1.1 Общие требования [rand.req.genl] в проекте стандарта C++ n4687 гласит:

В этом подпункте 29.6 действие создания экземпляра шаблона:

...

г) параметр типа шаблона с именем RealType не определен, если только соответствующий аргумент шаблона не является cv-неквалифицированным и не является одним из float, double, или длинный двойной.

Кроме того, 29.6.8.2.2 Шаблон класса uniform_real_distribution [rand.dist.uni.real] описывает std::uniform_real_distribution с параметром типа шаблона RealType и поэтому std::uniform_real_distribution<int> не определено:

template<class RealType = double>
class uniform_real_distribution {
    ...
};

Кроме того, аналогичное ограничение существует для std::uniform_int_distribution<T>. . Таким образом, нам нужно переключать тип распределения между std::uniform_real_distribution<T> и std::uniform_int_distribution<T> в зависимости от 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 и делаю функцию потокобезопасной, применяя принятый ответ в этот пост.

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)
person Hiroki    schedule 21.03.2019
comment
Это было именно то, что я искал. Большое спасибо! - person Alex Wicks; 21.03.2019