tags:

views:

1727

answers:

10

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.

+2  A: 

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.)

Chris Jester-Young
Make it more type safe.
Lennie De Villiers
To expand on "type safe", I'd say "to prevent me having to document and stick to a limited set of assumptions I make about the behaviour of arithmetic operators and conversions, which non-built-in types might not satisfy". Else you might change the implementation in future and want more assumptions.
Steve Jessop
So for example currently it uses +=. If it later changes to use +, then a user whose type only supports += will come a cropper. For an example this simple, I can't think of any other plausible implementation change, and overloading += and not + is asking for trouble anyway, but hopefully yswim.
Steve Jessop
onebyone: Hear, hear! Documenting the "interface" is the way to go, works much the same way as with dynamic languages.
Chris Jester-Young
+1  A: 

You may have to wait until C++0x compliant compilers are available so you can use concepts.

Mike Thompson
+2  A: 

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

kjensen
+4  A: 

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;
Jeff Hillman
Good work. http://lenniedevilliers.codepad.org/aR6xi14c
Lennie De Villiers
Steve Jessop
As is "long double", which might not be what you want. But that's just a question of listing every arithmetic type from the C++ standard.
Steve Jessop
A: 

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.

Lennie De Villiers
+1  A: 

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 :)

xtofl
+6  A: 

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

Lou Franco
You can also use BOOST_STATIC_ASSERT to enforce the condition at compile time.
ChrisN
This is a runtime check, which is not a good idea since this can be done at compile time instead.
Greg Rogers
SFINAE is the solution to this, not BOOST_STATIC_ASSERT
Leon Timmermans
There are ways to do static asserts oneself. (If you mind using boost) E.g. by using a switch(ASDF) {case 0: case SOMETHINGNOZZERO: break;}
Ronny
+1  A: 

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;
}
OldMan
A: 

Another stackoverflow thread on template constraints can be found here.

luke
+7  A: 
#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;
}
Leon Timmermans