我目前正在尝试实施类std::vector
类。这是一个学校项目:我们需要来编写它,即原始指针,没有STL结构std::vector
或std::valarray
。
因为我希望这个类尽可能通用,所以这是一个模板化的类。这是我的Tvector.h文件的摘录:
template <typename T>
class Tvector {
public:
Tvector();
// Other constructors and methods
void fill_randomly();
private:
T * _data;
std::size_t _data_size;
std::size_t _allocated_size;
};
模板的想法是我的矢量可以存储float
,double
,int
,std::size_t
,std::complex<double>
类型的数据(我的那些)需要的时候)如果可能的话,甚至可能更多。
我目前面临的问题是void Tvector<T>::fill_randomly()
方法的实现,就像它的名字所说的那样,它应该用随机数据填充我的向量。
template <typename T>
void Tvector<T>::fill_randomly() {
std::random_device rand_device;
if( std::is_integral<T>::value ) {
std::uniform_int_distribution<T> distribution_int;
// Then fill the vector
for(std::size_t i{0}; i < this->_data_size; ++i)
this->_data[i] = distribution(rand_device);
} else if( std::is_floating_point<T>::value ) {
std::uniform_real_distribution<T> distribution_real;
// Then fill the vector
for(std::size_t i{0}; i < this->_data_size; ++i)
this->_data[i] = distribution(rand_device);
} else if( /* is complex type */ ) {
// More code looking like the 2 previous if-blocks
} else {
// Non-supported type
static_assert( true, "The given type for Tvector is not supported by fill_randomly." );
}
}
当然我尝试创建Tvector<double>
并在其上调用fill_randomly
:distribution_int
声明的编译错误。
我假设我的具有编译时条件的if结构将表现为预处理器指令,并且如果编译时条件返回false,则整个相应的块将被删除我的函数定义。但是看到这个编译错误,我认为我的第一个假设是假的。
经过一些研究,我发现我可以专门化我的模板方法,如:
template <>
void Tvector<int>::fill_randomly() {
std::random_device rand_device;
std::uniform_int_distribution<int> distribution;
// Then fill the vector
for(std::size_t i{0}; i < this->_data_size; ++i)
this->_data[i] = distribution(rand_device);
}
好的,好的,让我们为我需要的类型做这个。但经过近10次复制粘贴后,我对自己说“所有这些复制粘贴......不正是为什么模板在这里?为了避免复制粘贴并制作通用代码?”。
然后我在论坛和搜索引擎上搜索更多,以便找到类似的问题(以及“好的”解决方案!)。我找不到它。
如果可以,我希望我的fill_randomly
方法的实现与我的第一次尝试完全相同,并且可以轻松扩展和修改。
当然,我不会问实现本身,而是正确的方法。
有没有“好”的方式来做我想要的事情?比10或20个方法专业化(和复制粘贴)更好的东西?
PS:这是我在这个论坛上的第一篇文章。我试图尊重所有规则,我不确定结果。如果有任何问题(缺少信息,而不是通常的格式化......),请告诉我。
答案 0 :(得分:0)
我建议使用一组重载来填充对应于不同类型的数据。它简化了Tvector::fill_randomly()
。
// Non-member functions to fill random values in the data array.
template <template <class> typename Distribution, typename T>
void fill_random_values(T* data, size_t size)
{
std::random_device rand_device;
Distribution<T> distribution;
for(std::size_t i{0}; i < size; ++i)
data[i] = distribution(rand_device);
}
void fill_random_values(int* data, size_t size)
{
fill_random_values<std::uniform_int_distribution>(data, size);
}
void fill_random_values(long* data, size_t size)
{
fill_random_values<std::uniform_int_distribution>(data, size);
}
void fill_random_values(float* data, size_t size)
{
fill_random_values<std::uniform_real_distribution>(data, size);
}
void fill_random_values(double* data, size_t size)
{
fill_random_values<std::uniform_real_distribution>(data, size);
}
// Add other overloads of fill_random_values as necessary.
template <typename T>
void Tvector<T>::fill_randomly()
{
fill_random_values(_data, _data_size);
}
答案 1 :(得分:0)
编辑:编辑了帖子并找到了解决方案。请参阅斜体和&#34;解决方案&#34;一部分。
std::enable_if
注意 :查看最终解决方案。最终解决方案使用std::enable_if
。
由于模板的机制以及我的模板参数不在fill_randomly
签名中,解决方案无法正常工作。
有关问题的基本示例和简短(但更完整)说明,请参阅&#34;注释&#34;中的here。部分。
std::conditional
此解决方案已获得here。在我的具体情况下,它应该正常工作。但这有点丑陋(个人观点),特别是如果你有很长的条件。就我而言,如果我使用该解决方案,我的fill_randomly
代码将如下所示:
template <typename T>
void Tvector<T>::fill_randomly() {
std::random_device rand_device;
using my_distribution = std::conditional<std::is_integral<T>::value,
std::uniform_int_distribution<T>,
std::conditional<std::is_floating_point<T>::value
|| std::is_same< T, std::complex<float> >::value
|| std::is_same< T, std::complex<double> >::value
|| std::is_same< T, std::complex<long double> >::value,
std::uniform_real_distribution<T>,
void>
>;
my_distribution distribution;
// Then fill the vector
for(std::size_t i{0}; i < this->_data_size; ++i) {
if( std::is_floating_point<T>::value ) {
this->_data[i] = distribution(rand_device);
} else {
this->_data[i] = std::complex<typename T::value_type>( distribution(rand_device),
distribution(rand_device) );
}
}
}
其中:
std::complex
的行。我没有看过演员表(例如从复合到复杂),但我认为这不是最好的方法。
注意 :文字与原始帖子中的相同。这个&#34;解决方案&#34;并不是最好的解决方案,我找到的最佳解决方案将在之后解释。
最后,正如@R sahu提出的那样,唯一的&#34;好&#34;目前发现的解决方案I(以及我的C ++教授)专门针对我需要的每种类型,使用copy-paste。
所以我的最终实现将是这样的:
template <>
void Tvector<double>::fill_randomly() {
std::random_device rand_device;
std::uniform_real_distribution<double> distribution;
// Then fill the vector
for(std::size_t i{0}; i < this->_data_size; ++i)
this->_data[i] = distribution(rand_device);
}
template <>
void Tvector<std::complex<double>>::fill_randomly() {
std::random_device rand_device;
std::uniform_real_distribution<double> distribution;
// Then fill the vector
for(std::size_t i{0}; i < this->_data_size; ++i)
this->_data[i] = std::complex<double>( distribution(rand_device),
distribution(rand_device) );
}
每当我需要一个尚未实现的类型时,我会更新我的重载。
这很难看,但这是我找到的唯一解决方案。
感谢@mascoj在我的第一篇文章中的评论,这是我正在寻找的解决方案。该解决方案已成功测试。
最后,我的Tvector
课程如下:
template <typename T>
class Tvector {
public:
Tvector();
// Other constructors and methods
// Should be in .cpp, is here for simplicity of the post.
void fill_randomly() {
// Call to the ONLY implemented function fill_randomly_impl().
this->fill_randomly_impl();
}
private:
template <typename Integer = T>
typename std::enable_if< std::is_integral<Integer>::value, Integer >::type fill_randomly_impl() {
// Implementation of the radom filling for integers.
return Integer();
}
template <typename Real = T>
typename std::enable_if< std::is_floating_point<Real>::value, Real >::type fill_randomly_impl() {
// Implementation of the radom filling for real numbers.
return Real();
}
// Other definitions of fill_randomly_impl for other types like complex types.
T * _data;
std::size_t _data_size;
std::size_t _allocated_size;
};
首先,fill_randomly_impl
函数的存在只是因为fill_randomly
函数的签名和返回类型是固定的。如果这些没有修复,我可以直接在我的fill_randomly
函数上应用相同的技术,而不引入fill_randomly_impl
函数。
template
?如果没有行template <typename Integer = T>
,编译器会将不同的定义视为重载,并说该函数不能重载。
最后,这个解决方案对我来说很好,再次感谢大家:)