I wrote a sample program at http://codepad.org/ko8vVCDF that use a template function how do I retrict the template function to only use numbers? int, double etc.
Why would you want to restrict the types in this case? Templates allow "static duck typing", so anything allowed by what your sum
function in this case should be allowed. Specifically, the only operation required of T
is add-assignment and initialisation by 0, so any type that supports those two operations would work. That's the beauty of templates.
(If you changed your initialiser to T result = T();
or the like, then it would work for both numbers and strings, too.)
You may have to wait until C++0x compliant compilers are available so you can use concepts.
You could look into type traits (use boost, wait for C++0x or create your own).
I found the following on google: http://artins.org/ben/programming/mactechgrp-artin-cpp-type-traits.pdf
You can do something like this:
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 );
};
};
You will get an error if you try to create a NumbersOnly that doesn't have a ValidateType overload:
NumbersOnly<int> justFine;
NumbersOnly<SomeClass> noDeal;
I only want the function to work for numbers (int, double) and not string so I want the compiler to nag if the user of the function try to use a string... In this case if you add a string it will compile fine but give an error at run-time.
Indeed, there's no need to make it more stringent. Have a look at the string version (using the default constructor style advised by Chris Jester-Young) here...
Take care, too, for overflows - you might need a bigger type to contain intermediate results (or output results). Welcome to the realm of meta-programming, then :)
The only way to restrict a template is to make it so that it uses something from the types that you want, that other types don't have.
So, you construct with an int, use + and +=, call a copy constructor, etc.
Any type that has all of these will work with your function -- so, if I create a new type that has these features, your function will work on it -- which is great, isn't it?
If you want to restrict it more, use more functions that only are defined for the type you want.
Another way to try to restrict is by creating a traits template -- something like this
template<class T>
SumTraits
{
public:
const static bool canUseSum = false;
}
And then specialize it for the classes you want to be ok:
template<>
class SumTraits<int>
{
public:
const static bool canUseSum = true;
};
Then in your code, you can write
if (!SumTraits<T>::canUseSum) {
// throw something here
}
edit: as mentioned in the comments, you can use BOOST_STATIC_ASSERT to make it a compile-time check instead of a run-time one
That is how you do it.
Comment the template specialization for double for example.. and it will not allow you to call that function with double as parameter. The trick is that if you try to call sum with a type that is not among the specializations of IsNumber
, then the generic implementation is called, and that implementation makes something not allowed (call a private constructor).
The error message is NOT intuitive unless you rename the IsNumber
class to something that sounds like an error message.
#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;
}
#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 vector<T>& a)
{
typedef typename vector::<T>::size_type size_type;
T result = 0;
size_type size = a.size();
for(size_type i = 0; i < size; i++)
{
result += a[i];
}
return result;
}