14
votes

Let's assume we have a template function "foo":

template<class T>
void foo(T arg)
{ ... }

I can make specialization for some particular type, e.g.

template<>
void foo(int arg)
{ ... }

If I wanted to use the same specialization for all builtin numeric types (int, float, double etc.) I would write those lines many times. I know that body can be thrown out to another function and just call of this is to be made in every specialization's body, however it would be nicer if i could avoid writting this "void foo(..." for every type. Is there any possibility to tell the compiler that I want to use this specialization for all this types?

6
This is not directly an answer but it covers specialization versus overloading and might interest you: gotw.ca/publications/mill17.htmsellibitze

6 Answers

19
votes

You could use std::numeric_limits to see whether a type is a numeric type (is_specialized is true for all float and integer fundamental types).

// small utility
template<bool> struct bool2type { };

// numeric
template<typename T>
void fooImpl(T arg, bool2type<true>) {

}

// not numeric
template<typename T>
void fooImpl(T arg, bool2type<false>) {

}

template<class T>
void foo(T arg)
{ fooImpl(arg, bool2type<std::numeric_limits<T>::is_specialized>()); }
3
votes

You can use an approach with preprocessor.

foo.inc:

template<>
void foo(TYPE arg)
{ /* do something for int, double, etc. */ }

foo.h:

template<class T>
void foo(T arg)
{ /*do something */ }

#define TYPE int
#include "foo.inc"
#undef TYPE

#define TYPE double
#include "foo.inc"
#undef TYPE

etc.

3
votes

With boost:

#include <boost/type_traits/is_scalar.hpp>
#include <iostream>
#include <string>

namespace detail
{
    typedef const boost::true_type& true_tag;
    typedef const boost::false_type& false_tag;

    template <typename T>
    void foo(const T& pX, true_tag)
    {
        std::cout << "special: " << pX << std::endl;
    }

    template <typename T>
    void foo(const T& pX, false_tag)
    {
        std::cout << "generic: " << pX << std::endl;
    }
}

template <typename T>
void foo(const T& pX)
{
    detail::foo(pX, boost::is_scalar<T>());
}

int main()
{
    std::string s = ":D";
    foo(s);
    foo(5);
}

You can mostly easily do it without boost:

#include <iostream>
#include <string>

// boolean stuff
template <bool B>
struct bool_type {};

typedef bool_type<true> true_type;
typedef bool_type<false> false_type;

// trait stuff
template <typename T>
struct is_scalar : false_type
{
    static const bool value = false;
};

#define IS_SCALAR(x) template <> \
            struct is_scalar<x> : true_type \
            { \
                static const bool value = true; \
            };

IS_SCALAR(int)
IS_SCALAR(unsigned)
IS_SCALAR(float)
IS_SCALAR(double)
// and so on

namespace detail
{
    typedef const true_type& true_tag;
    typedef const false_type& false_tag;

    template <typename T>
    void foo(const T& pX, true_tag)
    {
        std::cout << "special: " << pX << std::endl;
    }

    template <typename T>
    void foo(const T& pX, false_tag)
    {
        std::cout << "generic: " << pX << std::endl;
    }
}

template <typename T>
void foo(const T& pX)
{
    detail::foo(pX, is_scalar<T>());
}

int main()
{
    std::string s = ":D";
    foo(s);
    foo(5);
}
0
votes

maybe you can define a default template function that will work on all native type, and delegate specialization of custom type to user

0
votes

You could write a small script (e.g., Perl) to generate the source file for you. Create an array containing all the types you want to specialize, and have it write out the function header for each. You can even embed the script execution in your makefile to automagically re-run it if you change something.

Note: this assumes the implementation of foo can be made trivial and similar for each type, e.g., simply calling out to the real implementation function. But it avoids a bunch of template/preprocessor mumbo-jumbo that might make a future maintainer scratch his head.

0
votes

Here's a refinement of Johannes solution, but easier to read. Just do the type check inside the function:

template<typename T>
void foo(T arg)
{
  if (numeric_limits<T>::is_specialized)  // not a runtime check - compile time constant
  {
  }
  else
  {
  }
}

Using type traits (which itself is template specialization) allows replacing a bunch of template specializations with a single, easy to read function template