Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Differences between template specialization and overloading for functions?

Tags:

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?

like image 704
rlbond Avatar asked Oct 02 '09 21:10

rlbond


People also ask

What is the difference between function overloading and templates?

What is the difference between function overloading and templates? Both function overloading and templates are examples of polymorphism features of OOP. Function overloading is used when multiple functions do quite similar (not identical) operations, templates are used when multiple functions do identical operations.

What are the differences between function template and template function?

Function Template is the correct terminology (a template to instantiate functions from). Template Function is a colloquial synonym. So, there's no difference whatsoever.

What is function template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

What is the advantage of using template function over function overloading?

@kushal Yes but with template, the compiler create a new function for every type used, while with function overloading you keep control of how many function exist in the final program. Template makes the program bigger.

What is the difference between template specialization and overloads?

In "simple" cases, they would behave identically, but they are some cases where they differ. A trivial difference, is the explicit call add<int> which will call the specialization and not the overload. will fail with the template specialization ( int and float, so T cannot be deduced)

Is it possible to overload a function template?

More often than not it's easier to overload than to specialize a function template. It is actually possible to do this by using a dedicated type trait template class like template<typename T> struct result { using type = T; }; with specialization for int case template<> struct result<int> { using type = long; }; as a returned type.

What is the advantage of using non-template overloads?

So overloads are more flexible in how you can define them. And there is no ambiguity for the compiler. The overload resolution mechanism favors a non-template over a template generated function if their argument types match. More often than not it's easier to overload than to specialize a function template.

What is the difference between template specialization and template signature?

@DarkAtom The ambiguity is settled by the rule that if a template exactly matches the signature of a non template, the non template wins in overload resolution. Other than differences in how overload resolution proceeds, a template specialization locks you into a signature.


2 Answers

I can only think of a few differences - here are some examples that don't necessarily cause harm (i think). I'm 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.

like image 131
Johannes Schaub - litb Avatar answered Oct 09 '22 13:10

Johannes Schaub - litb


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.

like image 33
mmx Avatar answered Oct 09 '22 12:10

mmx