【问题标题】:c++ : template class vs two classes : efficiencyc++:模板类 vs 两个类:效率
【发布时间】:2015-01-23 05:11:42
【问题描述】:

我尝试了两件事:

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&lt;double/unsigned int&gt;::get() 方法所需的时间时,我意识到它所花费的时间是从RandDouble::get()RandUnisignedint::get() 调用的时间的两倍多。

有一种方法可以通过调用方法来保持模板方法的灵活性,该调用方法与使用两个不同类的调用方法一样有效。

【问题讨论】:

  • 如下所述,问题在于指针间接。 C++ 模板实际上是在预处理器时创建的,并且在后台有效地为您创建了 2/3/4 个类。因此,模板并不比手工工具类慢(取决于实现) 我很好奇的一件事是,我看不到你在类中实际使用 mt_ 的位置,它需要比你正在使用的类型更高精度。换行“std::mt19937_64 mt_;”不合适吗?对于“T mt__;”
  • @chrispepper1989 我在 get 方法中使用 mt_...std::uniform_real_distribution&lt;double&gt;std::uniform_int_distribution&lt;unsigned int&gt; 都需要它

标签: c++ performance templates c++11


【解决方案1】:

问题可能是通过使用指向分布类的指针获得的间接性。尝试直接使用没有指针的类,或者最好做类似的事情

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

为了选择您需要的分发类型。 (这只是给你一个提示,类型检查肯定可以改进)。


解释: 上面的代码工作如下:std::conditional&lt;(1),(2),(3)&gt; 只是类型的静态 if 语句。如果第一个字段(1) 中的检查结果为真,则它采用第二个字段(2) 中的类型,否则它选择第三个字段(3) 中的类型。

如果模板参数Type 是整数类型,std::is_integral&lt;Type&gt;::value 将评估为true。因此,您的分发类型将是通常需要的std::uniform_int_distribution&lt;Type&gt;

如果Type 不是整数类型(而是浮点类型,但此处未检查),则std::uniform_real_distribution&lt;Type&gt; 用于分发类型。

示例 (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;
}

【讨论】:

  • 我第一次看到std::conditional...这是什么?如果我不使用指针,Rand 会创建两个分布,对吧?
  • 不,这就是 std::conditional 的用途。 _dist 的类型取决于 Type 的内容。
  • 好吧,我想我明白了……std::conditional 根据我的模板类创建不同类型的类型名……我应该在我的类里面还是外面写这个?
  • @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_;
};

【讨论】:

  • @PinkFloyd:它将与我回答中的方法一样有效。事实上,这只是获得相同行为的另一种方式,当需要扩展时(例如添加更多特定于类型的成员函数),这可能更容易理解和更灵活。 +1。
  • @Alexander Drichel:不过,最好不要精确指定类型(如doubleunsigned int),而是用整数和浮点类型分开。否则Rand&lt;int&gt;(0,1) 将无法编译。
  • 这段代码无法编译。 dist_ 不是 Rand 的成员,因此无法在该类的成员初始化程序列表中初始化。此外,由于TDist 是一个依赖基类,因此get() 中的调用需要以this-&gt; 为前缀,以使dist_ 成为依赖名称,否则名称查找将找不到基类成员。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-08
相关资源
最近更新 更多