Pack (тип erase) генератор случайных чисел

В библиотеке С++ 11 std есть несколько генераторов случайных чисел (RNG), каждый из которых реализует концепцию UniformRandomNumberGenerator. Затем они могут использоваться как аргумент для случайных распределений, см. Также эту документацию для обзора.

Преимущество этой конструкции заключается в том, что выбор базового двигателя RNG отменяется из-за его применения. Однако для разработки также требуется определение (а не только объявления) всех вызовов для RNG (если тип RNG должен оставаться неопределенным как параметр шаблона). Таким образом, в

struct complicated_random_distribution
{
  /*
     some data and auxiliary methods here
  */
  // complicated; may call RNG::operator() many times
  template<typename RNG>
  some_type operator()(RNG&gen) const;
};

элемент operator() не может быть просто реализован в отдельном модуле компиляции (CU), но должен быть доступен в том же файле заголовка (или один #include d из него).

Для отдельной реализации желательно, чтобы какой-то способ был пакет RNG таким же образом, как std::function<> упаковывает любой вызываемый объект. (Просто используя std::function и предоставляя значения для RNG::min() и RNG::max() в качестве аргументов функции, определенной в отдельном CU, является ограничительным и не позволит использовать, скажем, std::uniform_real_distribution<> внутри).

Как это можно сделать? Имеются ли реализации для этого? Будет ли библиотека std предоставлять это в будущем? Или я после красной селедки?


Редактировать Генераторы случайных чисел должны иметь static членов min() и max(), что делает стирание типа жестким или невозможным (GNU libstdС++ не делает этого предположения и стирания типа с нестатическими членами min() и max() работает, но не с LLVM libС++, в котором используются стандартные члены static). Есть ли способ решить эту проблему? Если нет, не означает ли это, что стандарт С++ имеет искаженный интерфейс для генераторов случайных чисел?

Ответ 1

Адаптируйте RNG с помощью independent_bits_engine и введите стирание адаптированного RNG. Вы полностью знаете, что такое independent_bits_engine min() и max().

Здесь эскиз:

struct RNG_wrapper {
    using result_type = std::uint32_t;
    static constexpr result_type min() { return 0; }
    static constexpr result_type max() { return 0xFFFFFFFF; }

    template<class RNG>
    using my_engine_type = std::independent_bits_engine<RNG, 32, result_type>;

    template<class RNG,
             class = std::enable_if_t<!std::is_same<std::decay_t<RNG>, RNG_wrapper>{}>>
    RNG_wrapper(RNG&& r)
        : rng(my_engine_type<std::decay_t<RNG>>(std::forward<RNG>(r))) {}

    result_type operator()() { return rng(); }
    std::function<result_type()> rng;
};

Ответ 2

Это работает в clang и gcc (с libstdС++), но это не строго a UniformRandomNumberGenerator, по той причине, что min и max не являются статическими.

#include <cassert>
#include <random>
#include <functional>
#include <iostream>

template<typename T>
class AnyUniformRandomNumberGenerator
{
    T mMin;
    T mMax;
    std::function<T()> mValue;
public:

    using result_type = T;

    template<typename UniformRandomNumberGenerator>
    AnyUniformRandomNumberGenerator(UniformRandomNumberGenerator uniformRandomNumberGenerator) :
    mMin(UniformRandomNumberGenerator::min()),
    mMax(UniformRandomNumberGenerator::max()),
    mValue([=]() mutable { return uniformRandomNumberGenerator(); })
    {}

    T operator()()
    {
        return mValue();
    }

    T min()
    {
        return mMin;
    }

    T max()
    {
        return mMax;
    }
};

int main()
{
    std::default_random_engine rng;
    AnyUniformRandomNumberGenerator<decltype(rng())> any{rng};

    assert(any() <= any.max());
    assert(any() >= any.min());

    std::uniform_int_distribution<> dist{1, 6};

    std::cout << dist(any);
}

НКА: http://rextester.com/ANAP79935

лязг http://rextester.com/YCIIR21607