模板类中的动态模板方法选择

Dynamic template method selection within template class

本文关键字:动态 模板方法 选择      更新时间:2023-10-16

我想创建一个模板随机数生成器类,它可以是整数类型或浮点类型。 为什么? 对于一个赋值,我写了一个累加函数(基本上与 std::accumulate 相同),我想制作一个测试工具,它可以是任何整数或浮点类型(例如,无符号|短|长|长长整型、浮点数、双精度)。 我们一直在研究模板,我正在尝试使用模板编程来做出动态编译时决策。 我可能以错误的方式接近这一点 - 任何建议/参考都非常感谢。

这是我的测试函数:

void testdrive() {
std::vector<int> vint(ELEMENTS);
std::vector<double> vflt(ELEMENTS);
RNG<int> intrng;
RNG<double> fltrng;
std::generate(vint.begin(), vint.end(), intrng)
std::generate(vflt.begin(), vflt.end(), fltrng)
std::cout << "Sum of " << printvec(vint) << "is " accum(vint) << "nn";
std::cout << "Sum of " << printvec(vflt) << "is " accum(vflt) << 'n';
}

我不知道如何使用我的类的模板编程来做到这一点。 我想做的是,如果该类型是 int 类型,请使用 uniform_int_distribution 并且它是 float|double use uniform_real_distribution。 我意识到这两者并不完全可以互换,但对于我想要做的事情来说,这很好。 这是我的班级:

template<typename T>
class RNG {
public:
RNG(T low=std::numeric_limits<T>::min(),
T high=std::numeric_limits<T>::max())
: rng_engine{rng_seed()}, rng_dist{low, high}, rng_low{low},
rng_high{high} { }
RNG(const RNG& r): rng_engine{rng_seed()}, rng_dist{r.rng_low,
r.rng_high}, rng_low{r.rng_low}, rng_high{r.rng_high} { }
T operator()() { return rng_dist(rng_engine); }
private:
std::random_device rng_seed;
std::mt19937 rng_engine;
template<typename U, typename=std::enable_if_t<std::is_integral<T>::value>>
std::uniform_int_distribution<T> rng_dist;
template<typename U, typename=std::enable_if_t<std::is_floating_point<T>::value>>
std::uniform_real_distribution<T> rng_dist;
T rng_low, rng_high;
};

此外,对于任何阅读这篇文章的人来说,我发现这本书对深入研究C++模板非常有帮助:C++模板 - 完整指南 2nd Edition (http://www.tmplbook.com/)

看看模板专业化。 在下面的代码中,专用struct Distribution选择要使用的std::uniform_*_distribution

#include <stdio.h>
#include <vector>
#include <algorithm>
#include <random>
#include <iostream>
template <class T>
class RNG
{
// primary template is designed for integers
template <class U>
struct Distribution
{
typedef std::uniform_int_distribution<U> Type;
};
// explicit specialization for float
template <>
struct Distribution<float>
{
typedef std::uniform_real_distribution<float> Type;
};
// explicit specialization for double
template <>
struct Distribution<double>
{
typedef std::uniform_real_distribution<double> Type;
};
std::random_device rng_source;
typename Distribution<T>::Type rng_dist;
public:
RNG(
T low = std::numeric_limits<T>::min(),
T high = std::numeric_limits<T>::max())
: rng_source{}
, rng_dist(low, high)
{
}
RNG(const RNG& rhs)
: rng_source{}
, rng_dist(rhs.rng_dist)
{
}
T operator()()
{
return rng_dist(rng_source);
}
};
int main()
{
const size_t ELEMENTS = 10;
std::vector<int> vint(ELEMENTS);
std::vector<double> vflt(ELEMENTS);
RNG<int> intrng(0, 100);
RNG<double> fltrng(0.0, 1.0);
std::generate(vint.begin(), vint.end(), intrng);
std::generate(vflt.begin(), vflt.end(), fltrng);
return 0;  <-- set a breakpoint here to see both vectors
}

这是我想出来的,但我更喜欢@fifoforlifo的答案:

template<typename T>                                                      
class RNG {                                                               
static_assert(std::is_arithmetic<T>::value,                           
"Only primitive numeric types supported.");             
public:                                                               
RNG(T low=std::numeric_limits<T>::min(),                          
T high=std::numeric_limits<T>::max())                         
: rng_engine{rng_seed()}, rng_dist{low, high}, rng_low{low},  
rng_high{high} { }                                          
RNG(const RNG& r): rng_engine{rng_seed()}, rng_dist{r.rng_low,    
r.rng_high}, rng_low{r.rng_low}, rng_high{r.rng_high} { }     
T max() { return rng_dist.max(); }                                
T min() { return rng_dist.min(); }                                
T operator()() { return rng_dist(rng_engine); }                   
private:                                                              
std::random_device rng_seed;                                      
std::mt19937 rng_engine;                                          
std::uniform_int_distribution<T> rng_dist;                        
T rng_low, rng_high;                                              
};                                                                        
// Specialize RNG                                                         
// Really want a generic way to support any floating point type           
// e.g., float, double, long double                                       
// And ideally this would all be in one template class...                 
template<>                                                                
class RNG<double> {                                                       
public:                                                               
RNG(double low=std::numeric_limits<double>::min(),                
double high=std::numeric_limits<double>::max())               
: rng_engine{rng_seed()}, rng_dist{low, high}, rng_low{low},  
rng_high{high} { }                                          
RNG(const RNG& r): rng_engine{rng_seed()}, rng_dist{r.rng_low,    
r.rng_high}, rng_low{r.rng_low}, rng_high{r.rng_high} { }     
double max() { return rng_dist.max(); }                           
double min() { return rng_dist.min(); }                           
double operator()() { return rng_dist(rng_engine); }              
private:                                                              
std::random_device rng_seed;                                      
std::mt19937 rng_engine;                                          
std::uniform_real_distribution<double> rng_dist;          
double rng_low, rng_high;                                         
};