Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit template function specializations with overloads: Why would you do it?

Suppose the following:

template <typename T> void foo (T*);   // #1
template <typename T> void foo (T);    // #2
template <> void foo (int*);           // #3

When introducing an explicit specialization of a base template which also has overloads, the specialization is not considered during overload resolution by design. I understand this.

But, given that I could make #3 a non-template overload and it would be then considered for overload resolution, why would I still want to do it as I have done above? Is there a valid use-case for the setup demonstrated above? The only thing I can think of is that if you did not rely on template type deduction, the non-template functions could not be used since they would not accept the <> syntax when you call them.

BTW I've only reviewed the rules for C++03. I'm not sure if/how C++11 changes these rules/behaviors.

like image 987
void.pointer Avatar asked Dec 03 '14 20:12

void.pointer


People also ask

What is the advantage of using template over function overloading?

Function overloading is used when multiple functions do similar operations; templates are used when multiple functions do identical operations. Templates provide an advantage when you want to perform the same action on types that can be different.

What is overloading of function template?

Template Function Overloading:The name of the function templates are the same but called with different arguments is known as function template overloading. If the function template is with the ordinary template, the name of the function remains the same but the number of parameters differs.

Why does the need of template specialization arise?

It is possible in C++ to get a special behavior for a particular data type. This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming.

What is the relationship between function templates and overloading?

A function template can overload non-template functions of the same name. In this scenario, the compiler first attempts to resolve a function call by using template argument deduction to instantiate the function template with a unique specialization.


2 Answers

Basically, I favor specialization to avoid the principal of least surprise. You want to allow the functions to be called explicitly or non-explicitly to support usage in the widest amount of code possible...and when explicitly instantiated, it should behave the same way as when it was not.

The following template function is an example of how you may want to explicitly select which function you want to call (despite all 3 taking the same argument).

template <typename T> void foo (T*){std::cout << 1 << std::endl;}   // #1
template <typename T> void foo (T){std::cout << 2 << std::endl;}    // #2
template <> void foo<int> (int* x){std::cout << 3 << std::endl;}    // #3
//void foo (int*){std::cout << 3 << std::endl;}           // #3

template <typename T>
void bar(void* x) {
   foo<T>(reinterpret_cast<T*>(x));
   foo<T*>(reinterpret_cast<T*>(x));
   foo(reinterpret_cast<T*>(x));
}


int main()
{
   cout << "Hello World" << endl; 
   bar<int>(NULL);
   return 0;
}

Without the specialization, this outputs 1,2,3 (the explicitly instantiated call is different than the overloaded call), while with the specialization you get 3,2,3 (the explicit instantiation is the same as the implicit call).

like image 132
IdeaHat Avatar answered Sep 29 '22 12:09

IdeaHat


#3 can be used to specialize template functions in one compilation unit without having to update other compilation units.

Let's say we have z.cpp which looks like this:

template <class T> void foo (T*) { puts("1"); }
template <class T> void foo (T) { puts("2"); }
template <> void foo (int*) { puts("3"); }
void foo(int*) { puts("4"); }

int dummy() {
    foo((int*)NULL);    
    foo<int>((int*)NULL);
    foo(4);    
    foo((long*)NULL);    
}

and y.cpp which looks like this:

#include <stdio.h>

template <class T> void foo (T*);
template <class T> void foo (T);

int main() {
    foo((int*)NULL);    
    foo<int>((int*)NULL);
    foo(4);    
    foo((long*)NULL);    
}

The first lines of main will refer to #3 and not #4. This means that we can specialize foo for other types in z.cpp without having to change y.cpp.

With #4, you would have to update y.cpp everytime you added a new overload.

like image 20
tohava Avatar answered Sep 29 '22 13:09

tohava