Chenna V
Chenna V

Reputation: 10473

Template specialization for a set of types

How do I specialize a template for a set of data types? For example:

template<typename T>
inline T getRatio(T numer, T denom){
    return (numer/denom);
}

I want to specialize it for a set of data types so it only works with int, long, double and float. So if the user would try to use this function with a char type, the compiler would throw error.

Upvotes: 4

Views: 3011

Answers (5)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361322

If you want to restrict your getRatio() function only for int, long, double and float, then you can use this function as well. It will generate "a meaningful" compilation error if you call it with,say, char type argument. The compilation error would be : this_type_is_not_allowed_in_getRatio .

//yourheader.h
template<typename T>
inline T getRatio(T numer, T denom)
{
    typedef typelist<int, typelist<long, typelist<double, float>>> allowedtypes;
    compile_time_checker<contains<allowedtypes, T>::result> this_type_is_not_allowed_in_getRatio;
    return (numer/denom);
}

It uses this header:

//metafunctions.h
template<typename H, typename T>
struct typelist
{
    typedef H Head;
    typedef T Tail;
};

template<typename T, typename Tail> 
struct contains
{
    static const bool result = false;
};

template<typename Head, typename Tail, typename T> 
struct contains<typelist<Head, Tail>, T>
{
    static const bool result = false || contains<Tail, T>::result;
};

template<typename T, typename Tail> 
struct contains<typelist<T, Tail>, T>
{
    static const bool result = true || contains<Tail, T>::result;
};

template<bool b> struct compile_time_checker;
template<> struct compile_time_checker<true> {};

Hope, it helps you. You can write all your code in just one function now!

Upvotes: 2

Edward Strange
Edward Strange

Reputation: 40859

It depends on what you want to do. If you want the compiler to simply fail to find an appropriate resolution for the function call you could use Flinsch's answer, which is probably best, or you can use SFINAE:

template < typename T > is_an_ok_type : boost::mpl::false_ {};
template < > is_an_ok_type<int> : boost::mpl::true_ {};
... etc...

template < typename T >
typename boost::enable_if< is_an_ok_type<T>,T >::type
get_ratio(T t1, T t2)
{
  return t1/t2;
}

If you want some sort of reasonably readable error instead you use a static assert; either static_assert (C++0x) or BOOST_STATIC_ASSERT.

Upvotes: 5

Drako
Drako

Reputation: 21

you might do this:

// declaration
template <typename T> inline T getRatio(T numer, T denom);

// specialization for long    
template <>
inline long getRatio<long>(long numer, long denom) { return (numer / denom); }
// specialization for float
template <>
inline float getRatio<float>(float numer, float denom) { return (numer, denom); }
// specialization for double
template <>
inline double getRatio<double>(double numer, double denom) { return (numer / denom); }

this will result in a linker error if getRatio is called with a type other than long, float or double.

Upvotes: 2

Nick Meyer
Nick Meyer

Reputation: 40272

There's no way built into the language to specify that a template can only be instantiated with a certain set of type parameters. However, this will fail to compile for any type that does not have an operator / defined, which may be enough for you.

When designing APIs, it's considered good practice to avoid surprising your user, and most users would be surprised if you told them they weren't allowed to compute the ratio of two things that can be divided!

If you really don't want the default behavior, Flinsch's answer is a good compromise.

Upvotes: -1

Flinsch
Flinsch

Reputation: 4341

As only the three data types long, double and float are relevant and their is no need for an additional generic version, just reject the template and provide three functions for long, double and float.

Upvotes: 1

Related Questions