views:

590

answers:

5

So, I know that there is a difference between these two tidbits of code:

template <typename T>
T inc(const T& t)
{
    return t + 1;
}

template <>
int inc(const int& t)
{
    return t + 1;
}

and

template <typename T>
T inc(const T& t)
{
    return t + 1;
}

int inc(const int& t)
{
    return t + 1;
}

I am confused as to what the functional differences between these two are. Can someone show some situations where these snippits act differently from each other?

+4  A: 

Template specialization is more generic than just overloading. You can specialize things like classes rather than just simple functions. Overloading only applies to functions.

UPDATE: To clarify more per AraK's comment, you are really comparing apples and oranges here. Function overloading is used to introduce the ability to have different functions share a single name, if they have different signatures. Template specialization is used to define a specific code snippet for a specific type parameter. You can't have a template specialization if you don't have a template. If you remove the first piece of code that declares the generic template, you'll receive a compile time error if you try to use template specialization.

So, the goal of template specialization is pretty different from a function overload. They just happen to behave similarly in your example while they are fundamentally different.

If you provide an overload, you are declaring an independent method that happens to have the same name. You are not preventing the template to be used with the specific type parameter. To demonstrate this fact, try:

template <typename T>
T inc(const T& t)
{
    return t + 1;
}

int inc(const int& t)
{
    return t + 42;
}

#include <iostream>
int main() {
   int x = 0;
   x = inc<int>(x);
   std::cout << "Template: " << x << std::endl; // prints 1.

   x = 0;
   x = inc(x);
   std::cout << "Overload: " << x << std::endl; // prints 42.
}

As you can see, in this example, there are two distinct inc functions for int values: inc(const int&) and inc<int>(const int&). You couldn't expand the generic template using int if you had used template specialization.

Mehrdad Afshari
Good note, although it doesn't answer the question.
AraK
AraK: The question, as I understand it, is basically, why there are two ways to achieve the same thing. It demonstrates a similar result achieved by two methods and asks for the difference between the two methods. My answer is that these two methods apply to different contexts and a single example where the two are applicable does not make them equivalent.
Mehrdad Afshari
I said it is a good note, but doesn't answer the difference in functionality between *function template specialization* and *function overloading*. You are talking about template specialization in general. I think the title is misleading little bit.
AraK
I knew this already; I was specifically referring to what is the difference between function overloading in the presence of a templated function with the same name vs. function template specialization.
rlbond
+1  A: 

AFAIK there is no functional difference. All I can add is that if you have both a template function specialisation and an ordinary function defined then there is no overload ambiguity as the ordinary function is favoured.

Troubadour
+2  A: 

One such example:

#include <cstdio>

template <class T>
void foo(T )
{
    puts("T");
}

//template <>
void foo(int*)
{
    puts("int*");
}

template <class T>
void foo(T*)
{
    puts("T*");
}

int main()
{
    int* a;
    foo(a);
}

It is actually suggested that you use non-template overloads for functions and leave specialization for classes. It is discussed at greater length in Why Not Specialize Function Templates?

UncleBens
+6  A: 

I can only think of a few differences - here are some but that don't necessarily cause harm i think (omitting definitions to keep it terse)

template <typename T> T inc(const T& t);
namespace G { using ::inc; }
template <> int inc(const int& t);
namespace G { void f() { G::inc(10); } } // uses explicit specialization

// --- against ---

template <typename T> T inc(const T& t);
namespace G { using ::inc; }
int inc(const int& t);
namespace G { void f() { G::inc(10); } } // uses template

That is because specializations are not found by name lookup, but by argument matching, so a using declaration will automatically consider a later introduced specialization.

Then, you of course cannot partially specialize function templates. Overloading however accomplishes something very similar by partial ordering (using different types now, to make my point)

template <typename T> void f(T t); // called for non-pointers
template <typename T> void f(T *t); // called for pointers.

int a;
void e() {
  f(a); // calls the non-pointer version
  f(&a); // calls the pointer version
}

That wouldn't be possible with function template explicit specialization. Another example is when references are involved, which causes template argument deduction to look for an exact match of the types involved (modulo base/derived class relationships and constness):

template<typename T> void f(T const &);
template<> void f(int * const &);

template<typename T> void g(T const &);
void g(int * const &);

int a[5];
void e() {
  // calls the primary template, not the explicit specialization
  // because `T` is `int[5]`, not `int *`
  f(a);

  // calls the function, not the template, because the function is an
  // exact match too (pointer conversion isn't costly enough), and it's 
  // preferred. 
  g(a);
}


I recommend you to always use overloading, because it's richer (allows something like partial specialization would allow), and in addition you can place function in whatever namespace you want (although then it's not strictly overloading anymore). For example, instead of having to specialize std::swap in the std:: namespace, you can place your swap overload in your own namespace and make it callable by ADL.

Whatever you do, never mix specialization and overloading, it will be a hell of a mess like this article points out. The Standard has a lovely paragraph about it

The placement of explicit specialization declarations for function templates, class templates, member functions of class templates, static data members of class templates, member classes of class templates, member class templates of class templates, member function templates of class templates, member functions of member templates of class templates, member functions of member templates of non-template classes, member function templates of member classes of class templates, etc., and the placement of partial specialization declarations of class templates, member class templates of non-template classes, member class templates of class templates, etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

Johannes Schaub - litb
Great article..
rlbond
+1 nice - I likes :)
Faisal Vali
thanks folks :)
Johannes Schaub - litb
@Johannes Schaub :Superb Answer..WOW
BE Student
A: 

Just to elaborate on the first point mentioned by litb in his answer. Specializations are only checked once overload resolution has actually selected a primary template. The result can lead to some surprises where a function is overloaded and has explicit specializations:

template <typename T> void foo (T);  // Primary #1
template <> void foo<int*> (int*);   // Specialization of #1

template <typename T> void foo (T*); // Primary #2

void bar (int * i)
{
  foo(i);
}

When choosing which function to call, the following steps take place:

  1. Name lookup finds both primary templates.
  2. Each template is specialized and overload resolution attempts to select a best function based on conversions between the arguments and parameters.
  3. In thise case, there is no difference in the quality of the conversions.
  4. Partial ordering rules are then used to select the most specialized template. In this case that is the second parimary "foo(T*)".

Only after these steps, when the best function has been selected will explicit specializations of the selected function be considered. (In this case primary #2 has none so none are considered).

The only way to call the above explicit specialization here, is to actually use explicit template arguments in the call:

void bar (int * i)
{
  foo<int*> (i);  // expliit template argument forces use of primary #1
}

A good rule of thumb is to try to avoid having overloads that are also explicily specialized, as the resulting rules are pretty complex.

Richard Corden