限制模板功能
我在http://codepad.org/ko8vVCDF上编写了一个使用模板功能的示例程序。
如何限制模板函数仅使用数字? (int,double等)
#include <vector> #include <iostream> using namespace std; template <typename T> T sum(vector<T>& a) { T result = 0; int size = a.size(); for(int i = 0; i < size; i++) { result += a[i]; } return result; } int main() { vector<int> int_values; int_values.push_back(2); int_values.push_back(3); cout << "Integer: " << sum(int_values) << endl; vector<double> double_values; double_values.push_back(1.5); double_values.push_back(2.1); cout << "Double: " << sum(double_values); return 0; }
解决方案
在这种情况下,为什么要限制类型?模板允许"静态鸭子输入",因此在这种情况下,应允许sum
函数所允许的任何事情。具体来说," T"所需的唯一操作是加法赋值和0初始化,因此支持这两个操作的任何类型都可以使用。那就是模板的美。
(如果我们将初始化程序更改为T result = T();
之类的东西,那么它也适用于数字和字符串。)
我们可以研究类型特征(使用boost,等待C ++ 0x或者创建自己的特征)。
我在Google上找到了以下内容:http://artins.org/ben/programming/mactechgrp-artin-cpp-type-traits.pdf
我们可以执行以下操作:
template <class T> class NumbersOnly { private: void ValidateType( int &i ) const {} void ValidateType( long &l ) const {} void ValidateType( double &d ) const {} void ValidateType( float &f ) const {} public: NumbersOnly() { T valid; ValidateType( valid ); }; };
如果尝试创建没有ValidateType重载的NumbersOnly,则会收到错误消息:
NumbersOnly<int> justFine; NumbersOnly<SomeClass> noDeal;
确实,没有必要对其进行更严格的规定。在这里查看字符串版本(使用Chris Jester-Young建议的默认构造函数样式)...
还要注意,对于溢出,我们可能需要更大的类型才能包含中间结果(或者输出结果)。欢迎来到元编程领域,然后:)
限制模板的唯一方法是制作模板,以便它使用所需类型中的某些内容,而其他类型则没有。
因此,我们可以使用int构造,使用+和+ =,调用复制构造函数等。
具有所有这些功能的任何类型都可以与函数一起使用-因此,如果我创建一个具有这些功能的新类型,则函数将可以使用它-很好,不是吗?
如果要进一步限制它,请使用更多仅针对所需类型定义的功能。
实现此目的的另一种方法是创建一个traits模板-这样的东西
template<class T> SumTraits { public: const static bool canUseSum = false; }
然后将其专门用于我们想要的课程:
template<> class SumTraits<int> { public: const static bool canUseSum = true; };
然后在代码中,我们可以编写
if (!SumTraits<T>::canUseSum) { // throw something here }
编辑:如注释中所述,我们可以使用BOOST_STATIC_ASSERT使其成为编译时检查,而不是运行时检查
那就是你要做的。
例如,将模板专业化注释为double注释。并且不允许使用double作为参数调用该函数。诀窍是,如果我们尝试使用不属于" IsNumber"专业化类型的类型来调用sum,则将调用通用实现,并且该实现将使某些事情不被允许(称为私有构造函数)。
除非我们将IsNumber
类重命名为听起来像是错误消息的内容,否则错误消息是不直观的。
#include <vector> #include <iostream> using namespace std; template<class T> struct IsNumber{ private: IsNumber(){} }; template<> struct IsNumber<float>{ IsNumber(){}; }; template<> struct IsNumber<double>{ IsNumber(){}; }; template<> struct IsNumber<int>{ IsNumber(){}; }; template <typename T> T sum(vector<T>& a) { IsNumber<T> test; T result = 0; int size = a.size(); for(int i = 0; i < size; i++) { result += a[i]; } return result; } int main() { vector<int> int_values; int_values.push_back(2); int_values.push_back(3); cout << "Integer: " << sum(int_values) << endl; vector<double> double_values; double_values.push_back(1.5); double_values.push_back(2.1); cout << "Double: " << sum(double_values); return 0; }
通过使用SFINAE,这是可能的,而通过使用Boost或者C ++ 11的帮助程序,这将变得更加容易
促进:
#include <vector> #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_arithmetic.hpp> template<typename T> typename boost::enable_if<typename boost::is_arithmetic<T>::type, T>::type sum(const std::vector<T>& vec) { typedef typename std::vector<T>::size_type size_type; T result; size_type size = vec.size(); for(size_type i = 0; i < size; i++) { result += vec[i]; } return result; }
C ++ 11:
#include <vector> #include <type_traits> template<typename T> typename std::enable_if<std::is_arithmetic<T>::value, T>::type sum(const std::vector<T>& vec) { T result; for (auto item : vec) result += item; return result; }