2014-11-25 4 views
0

Я попробовал две вещи:C++: шаблон класса против двух классов: эффективность

class RandDouble{ 
    public: 
     RandDouble(double const& min_inclusive, double const& max_exclusive): 
       mt_(std::random_device()), 
       dist_(min_inclusive,max_exclusive) 
     {} 
     ~RandDouble(){} 
     double get(){ return dist_(mt_); } 

    private: 
     std::mt19937_64 mt_; 
     std::uniform_real_distribution<double> dist_; 
}; 

class RandUnsignedInt{ 
    public: 
     RandUnsignedInt(unsigned int const& min_inclusive, unsigned int const& max_inclusive): 
       mt_(std::random_device()), 
       dist_(min_inclusive,max_exclusive) 
     {} 
     ~RandUnsignedInt(){} 
     unsigned int get(){ return dist_(mt_); } 

    private: 
     std::mt19937_64 mt_; 
     std::uniform_int_distribution<unsigned int> dist_; 
}; 

И

template<typename Type> 
class Rand{ 
    public: 
     Rand(Type const& min_inclusive, Type const& max_exclusive);/ 
     ~Rand(); 
     Type get(); 

    private: 
     std::mt19937_64 mt_; 
     std::uniform_real_distribution<double>* dist_double_; 
     std::uniform_int_distribution<unsigned int>* dist_u_int_; 
}; 
template<typename Type> 
Rand<Type>::~Rand(){ 
    if(dist_double_){ delete dist_double_; } 
    if(dist_u_int_){ delete dist_u_int_; } 
} 

с файлом .cpp:

template<> 
Rand<double>::Rand(double const& min_inclusive, double const& max_exclusive): 
    mt_(std::random_device()()), 
    dist_double_(new std::uniform_real_distribution<double>(min_inclusive,max_exclusive)), 
    dist_u_int_(NULL) 
{} 

template<> 
Rand<unsigned int>::Rand(unsigned int const& min_inclusive, unsigned int const& max_exclusive): 
    mt_(std::random_device()()), 
    dist_double_(NULL), 
    dist_u_int_(new std::uniform_int_distribution<unsigned int>(min_inclusive,max_exclusive)) 
{} 

template<> 
double Rand<double>::get(){ return (*dist_double_)(mt_); } 

template<> 
unsigned int Rand<unsigned int>::get(){ return (*dist_u_int_)(mt_); } 

С практической точки зрения , решение шаблона более гибко с другими классами шаблонов, потому что я мог бы делать такие вещи, как:

template<typename Type> 
classs C{ 
    /*some code*/ 
    private: 
     Rand<Type> r; 
}; 

Мне нравится решение для шаблона. Но когда я проверяю время, необходимое для вызова метода Rand<double/unsigned int>::get(), я понял, что он занимает более двух раз больше времени, чем звонок от RandDouble::get() или RandUnisignedint::get().

Это способ сохранить гибкость метода шаблона с помощью метода вызова, который так же эффективен, как и у двух разных классов.

+0

Как упомянуто ниже, проблема с указателем косвенности. Шаблоны C++ на самом деле создаются в режиме предварительного процессора и эффективно делают 2/3/4 классы для вас под капотом. Поэтому шаблоны не медленнее, чем ручные инструментальные классы (в зависимости от реализации) Мне очень интересно, я не вижу, где вы фактически используете mt_ внутри класса, который нуждается в более высокой точности, чем тот тип, который вы используете. Неправильно ли было бы заменить строку "std :: mt19937_64 mt_;" для "T mt__;" – chrispepper1989

+0

@ chrispepper1989 Я использую mt_ в методе get ...это необходимо либо 'std :: uniform_real_distribution ', либо 'std :: uniform_int_distribution ' – PinkFloyd

ответ

5

Проблема, вероятно, в косвенности, которую вы получаете с помощью указателей на классы распределения. Попробуйте использовать классы напрямую, без указателей, или, может быть, лучше сделать что-то вроде

typename std::conditional<std::is_integral<Type>::value 
         , std::uniform_int_distribution<Type> 
         , std::uniform_real_distribution<Type> >::type _dist; 

для того, чтобы выбрать тип распределения вам нужно. (Это просто, чтобы дать вам подсказку, проверка типа, безусловно, может быть улучшена).


Объяснение: Код выше работает следующим образом: std::conditional<(1),(2),(3)> просто как статический Условный оператор для типов. Если проверка в первом поле (1) оценивается как истина, она берет тип во втором поле (2), в противном случае он выбирает тип в третьем поле (3).

В случае, если параметр шаблона Type является целым числом, std::is_integral<Type>::value будет оцениваться как true. Таким образом, тип вашего дистрибутива будет std::uniform_int_distribution<Type>, который обычно желателен.

В случае Type это не интегральный тип (а скорее тип с плавающей точкой, который, однако, здесь не проверяется), вместо этого используется тип std::uniform_real_distribution<Type>.

Пример (tested here):

#include<random> 
#include<iostream> 

template<typename Type> 
struct UniformDistribution 
{ 
    std::mt19937_64 mt_; 
    typename std::conditional<std::is_integral<Type>::value 
          , std::uniform_int_distribution<Type> 
          , std::uniform_real_distribution<Type> >::type dist_;  
    Type get() 
    { 
     return dist_(mt_); 
    } 
}; 

int main() 
{ 
    //produces uniformly distributed integer number in [0, numeric_limist<int>::max()] 
    std::cout<<UniformDistribution<int>().get()<<std::endl; 

    //produces uniformly distributed double number in [0,1] 
    std::cout<<UniformDistribution<double>().get()<<std::endl; 
} 
+0

Впервые я вижу 'std :: условный' ... что это? И если я не использую указатель, Рэнд создаст два дистрибутива, верно? – PinkFloyd

+0

Нет, это то, для чего предназначен std :: условный. Тип _dist будет тем или иным в зависимости от содержимого Type. – DanielM

+0

ОК, я думаю, я понимаю ... 'std :: условный' создает имя типа другого типа в зависимости от моего класса шаблона ... должен ли я писать это внутри или вне моего класса? – PinkFloyd

2
#include<random> 

template< class T > 
struct TDist{}; 

template<> struct TDist<double> { std::uniform_real_distribution<double> dist_; }; 
template<> struct TDist<unsigned int> { std::uniform_int_distribution<unsigned int> dist_; }; 


template<typename Type> 
class Rand : private TDist<Type> { 
public: 
    Rand(Type min_inclusive, Type max_exclusive) : 
     mt_(std::random_device()), 
     dist_(min_inclusive,max_exclusive) 
    {} 

    Type get(){ return dist_(mt_); } 

private: 
    std::mt19937_64 mt_; 
}; 
+0

Спасибо, я постараюсь понять, насколько эффективен ваш метод ... – PinkFloyd

+0

@PinkFloyd: Он будет таким же эффективным, как подход в моем ответе. Фактически, это просто еще один способ получить такое же поведение, которое, вероятно, легче понять и более гибко, когда требуется расширение (например, добавление дополнительных функций-членов типа). +1. – davidhigh

+0

@ Alexandr Drichel: по-прежнему было бы лучше не указывать типы точно (как 'double' и' unsigned int'), а скорее отдельные по типам с плавающей запятой. В противном случае 'Rand (0,1)' не скомпилируется. – davidhigh

Смежные вопросы