Перегруженный шаблон случайный номер функции, используя современный c++


Я не возиться много с шаблонами (или с GTK), но сегодня я что-то написал и хотел бы знать, как это может быть улучшено, и надеюсь прояснить некоторые моменты в процессе.

Так, Randolfi() это шаблон функции, которая возвращает случайное число. Диапазон может быть задан в качестве аргумента.

Он использует новые возможности C++11, которые, исходя из этих:

(Название "Рандольфи" - это шутка, сделанные несколько коллег по поводу моей фамилии, Ranolfi.)

рандольфи.ГЭС

#ifndef RANDOLFI_HPP
#define RANDOLFI_HPP

#include <random>

template <typename T>
T Randolfi()
{
    std::mt19937 rng;
    rng.seed( std::random_device()() );
    std::uniform_int_distribution<T> dist;
    return dist(rng);
}

template <typename T>
T Randolfi(T range_start, T range_end)
{
    std::mt19937 rng;
    rng.seed( std::random_device()() );
    std::uniform_int_distribution<T> dist(range_start, range_end);
    return dist(rng);
}

#endif //RANDOLFI_HPP

randolfi-test.cpp

#include <iostream>
#include <string>
#include <gtkmm.h>
#include "randolfi.hpp"

using namespace std;

int main()
{
    auto app = Gtk::Application::create();

    uint a_random_number = Randolfi<uint>();

    Gtk::MessageDialog dialog( to_string(a_random_number) );    
    dialog.run();


    return 0;
}

Примечания:

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

  2. Я хоть и о признании его как Randolfi(T range_start = NULL, T range_end = NULL) а потом проверять if (range_start != NULL && range_end != NULL)но это не спасет меня ни строчки, и также , как представляется, не одобряется.

  3. Как это так, функция может быть вызвана без аргумента типа для второй перегрузки, но не для первого:

    Randolfi(0, 100);  // type parameter ommited for template
    Randolfi();  // Fails - compiler has no clue on know how to instantiate template
    

    У меня есть ощущение, что это несоответствие является плохой практикой.

  4. Я в основном озабочен шаблон заголовка, но я включить моего тестирования кода (randolfi-test.cpp) на этот вопрос так может кто-то может, дайте мне знать, если я сделал что-то ужасно неправильно. Со своей стороны, я бы хотел, чтобы избежать вызова Gtk::Application::create() но это не представляется возможным.

Спасибо!



Комментарии
2 ответа

Конструктор uniform_int_distribution по умолчанию аргументы:

template<typename T>
explicit uniform_int_distribution(T a = 0,
T b = std::numeric_limits<T>::max());

(Я не знаю, почему a по умолчанию 0, а не к минимуму Tно там вы идете).

Вы можете сократить дублирования, используя те же аргументы по умолчанию, и пропуская их через этот конструктор:

template <typename T>
T Randolfi(T range_start = 0,
T range_end = std::numeric_limits<T>::max())
{
std::uniform_int_distribution<T> dist(range_start, range_end);
return dist(rng);
}

Или, мы можем использовать Perfect Forwarding в создавать распределение с теми же аргументами, что принят в:

template <typename T, typename... Args>
T Randolfi(Args&&... args)
{
std::uniform_int_distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);
}

Мы должны сделать это, если мы хотим поддержать различные дистрибутивы, так это, вероятно, способ пойти.


Не создавать генератор случайных каждом вызове. Вы могли бы сделать это static, который будет сократить до одного раза в инстанцировании, но это лучше, чтобы держать его вне функции, так что вы только создать и семя это еще один процесс.

В тестовой программе, вы должны проявлять более чем один тип Tи тест с имплицитной и эксплицитной серии. Кроме того, избегайте using namespace std;и предпочитаю тесты, которые не требуют графического окружения (поэтому они могут быть более легко выполняются на серверах сборки).


Измененный код

#include <limits>
#include <random>
#include <utility>

static auto rng = []{
std::mt19937 rng;
rng.seed(std::random_device()());
return rng;
}();

template <typename T,
template<typename> typename Distribution
= std::uniform_int_distribution,
typename... Args>
T Randolfi(Args&&... args)
{
Distribution<T> dist(std::forward<Args>(args)...);
return dist(rng);
}

#include <iostream>

int main()
{
std::clog << Randolfi<int>(-100, +100) << '\n'
<< Randolfi<char>('a', 'z') << '\n'
<< Randolfi<double, std::normal_distribution>(100, 15) << '\n'
<< Randolfi<std::size_t>() << std::endl;
}

4
ответ дан 13 апреля 2018 в 10:04 Источник Поделиться

Как она стоит сейчас, каждый раз, когда требуется случайное число, вы создаете новый экземпляр std::mt19937затем, используя std::random_device для семян этого экземпляра, затем (наконец-то) получаю количество и возвращая его.

Что вы (почти всегда) хочу сделать, это создать один экземпляр std::mt19937семя его один раз и повторно использовать тот же генератор на протяжении оставшейся части программы.

Как она стоит сейчас, вы будете гораздо лучше просто использовать std::random_device чтобы создать номер (без использования std::mt19937 на всех):

template <typename T>
T Randolfi()
{
return std::random_device()();
}

...в какой момент, кода мог бы также существовать вообще, потому что это не добавляет ничего к тому, что std::random_device предоставляет из коробки.

3
ответ дан 13 апреля 2018 в 03:04 Источник Поделиться