15

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?

peper0
  • 3,111
  • 23
  • 35
  • This is not directly an answer but it covers specialization versus overloading and might interest you: http://www.gotw.ca/publications/mill17.htm – sellibitze Mar 12 '10 at 08:23

6 Answers6

19

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>()); }
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Neat idea! But why not just make `fooImpl` depend on one type and one boolean argument? – Vlad Mar 12 '10 at 02:23
  • 1
    @Vlad because we want compile time branching. If we pass a `bool` function argument, we will have the code executed in the same function and use `if` to branch. That will unfortunately type-check both branches, and very likely miss the goal. – Johannes Schaub - litb Mar 12 '10 at 02:26
  • @Johannes: I meant two template arguments, just to spare `bool2type` definition. – Vlad Mar 12 '10 at 02:28
  • 1
    @Vlad, we can't do that because function templates cannot be partially specialized. – Johannes Schaub - litb Mar 12 '10 at 02:29
  • This will work as long as you do not specialize `std:: numeric_limits` for custom type. I'd use `boost::is_fundamental`. – Kirill V. Lyadvinsky Mar 12 '10 at 05:16
  • boost::is_fundamental includes void; you want is_arithmetic; or, if you have access to std::tr1, there is std::tr1::is_arithmetic. Both have many other similar type traits. – Patrick Johnmeyer Mar 12 '10 at 05:53
  • @Kirill, right you could specialize for a custom type. But if it is a numeric custom type, it could fit the bill too. But indeed if you really only want the fundamental numeric types, a dedicated template looks better. – Johannes Schaub - litb Mar 12 '10 at 13:45
3

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.

Vlad
  • 35,022
  • 6
  • 77
  • 199
3

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);
}
GManNickG
  • 494,350
  • 52
  • 494
  • 543
0

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

YeenFei
  • 3,180
  • 18
  • 26
0

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.

Michael Kristofik
  • 34,290
  • 15
  • 75
  • 125
0

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

Yale Zhang
  • 1,447
  • 12
  • 30