34

Can anybody summarize the idea of function template overloading? What matters, template parameter or function parameter? What about the return value?

For example, given a function template

template<typename X, typename Y> void func(X x, Y y) {}

what's the overloaded function template?

1) template<typename X> void func(X x, int y) {}
2) template<typename X, typename Y> X func(X x, Y y) {}
3) template<class X, class Y, class Z> void func(X x, Y y, Z z) {}
Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
skydoor
  • 25,218
  • 52
  • 147
  • 201

5 Answers5

50

Of that list only the second introduces ambiguity, because functions - regardless of whether they are templates - can't be overloaded based on return type.

You can use the other two:

template<typename X> void func(X x, int y);

will be used if the second argument of the call is an int, e.g func("string", 10);

template<class X, class Y, class Z> void func(X x, Y y, Z z);

will be used if you call func with three arguments.


I don't understand why some other answers mentions that template functions and function overloading doesn't mix. They certainly do, and there are special rules how the function to call is selected.

14.5.5

A function template can be overloaded with other function templates and with normal (non-template) functions. A normal function is not related to a function template (i.e., it is never considered to be a specialization), even if it has the same name and type as a potentially generated function template specialization.)

A non-templated (or "less templated") overload is preferred to templates, e.g

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

foo(10); //calls void foo(int)
foo(10u); //calls void foo(T) with T = unsigned

Your first overload with one non-template parameter also falls under this rule.

Given choice between several templates, more specialized matches are preferred:

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

int i;
int* p;
int arr[10];

foo(i);  //calls first
foo(p);   //calls second
foo(arr); //calls second: array decays to pointer

You can find a more formal description of all the rules in the same chapter of the standard (Function templates)


And finally there are some situations where two or more overloads would be ambiguous:

template <class T> void foo(T, int);
template <class T> void foo(int, T);

foo(1, 2);

Here the call is ambiguous, because both candidates are equally specialized.

You can disambiguate such situations with the use of (for example) boost::disable_if. For example, we can specify that when T = int, then the second overload shouldn't be included as an overload candidate:

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
template <class T>
void foo(T x, int i);

template <class T>
typename boost::disable_if<boost::is_same<int, T> >::type
foo(int i, T x);

foo(1, 2); //calls the first

Here the library produces a "substitution failure" in the return type of the second overload, if T = int, removing it from the set of overload candidates.

In practice you should rarely run into situations like that.

visitor
  • 8,564
  • 2
  • 26
  • 15
  • 1
    Note that, for `template void func(T); template void func(T*);` it is also possible to make a version that specifically handles arrays: `template void func((T&)[N]);` This prevents the array from decaying into a pointer and calling the pointer version. – Chris Lutz Feb 01 '10 at 21:36
  • I am trying to use `template class_name::class_name(const T&);` and `template class_name::class_name(const T*);` to instantiate a class object, but when ever I pass a char array it tries to call `class_name(const T&)` instead of `class_name(const T*)`. I am getting a error message include/Class_name.h|67|error: no matching function for call to ‘to_string(const char [43])’| because, in `class_name(const T&)` I'm using `to_string(T)` while in `class_name(const T*)` I am specially handing char arrays. – Gaurish Gangwar Jul 19 '17 at 03:59
  • @GaurishGangwar You should've posted your own question. But yes, the overload taking the array as-is by reference is a better conversion than letting it decay to a pointer. The reason it decays to a pointer in the case that Chris gave is because he proposed a scenario that passed by value, which isn't possible for raw C-style arrays and therefore would enforce decay to a pointer. – underscore_d Nov 18 '18 at 16:08
4

There are two separate things here: function templating and function overloading. Any two distinct template declarations are likely to be overloads of each other, so your question doesn't quite make sense as stated. (The three "overloads" you give do not build upon the first template, rather you have four overloads to the same function name.) The real issue is, given some overloads and a call, how to call the desired overload?

First, the return type doesn't participate in the overloading process whether or not there is a template involved. So #2 will never play well with #1.

Second, the rules for function template overload resolution are different from the more commonly used class template specialization rules. Both essentially solve the same problem, but

  • the rules for class templates are simpler and more powerful, allowing for example recursion, and (member) functions differing only by return type
  • the rules for function templates allow the compiler to figure the template arguments from the function argument types

You might be able to solve your particular problem with function template overloads, but you may have trouble fixing any bug that arises as the rules are longer and fewer people are familiar with their intricacies. I was unaware after a few years of template hacking that subtle function template overloading was even possible. In libraries such as Boost and GCC's STL, an alternative approach is ubiquitous. Use a templated wrapper class:

template< typename X, typename Y >
struct functor {
    void operator()( X x, Y y );
};
template< typename X > // partial specialization: 
struct functor< X, int > { // alternative to overloading for classes
    void operator()( X x, int y );
};

Now you sacrifice the implicit instantiation syntax (with no angle brackets). If you want to get that back, you need another function

template< typename X, typename Y > void func( X x, Y y ) {
    return functor< X, Y >()( x, y );
}

I'd be interested to hear whether function overloading can do anything (besides deduction) that class [partial] specialization can't…

And then, of course, your overload #3 will never face ambiguity because it has a different number of arguments than any other overload.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Your example doesn't seem to be ambiguous. The call matches the second (one parameter matches int exactly). Perhaps you meant overloads like: `template void func(int x, X y); template void func(X x, int y);` – UncleBens Feb 01 '10 at 08:02
  • Second `func` will reject some of the arguments accepted by the first `func` but not the other way around, which makes the template more specialized. For a plain function call, if we would ignore this ordering, the call would be ambiguous, because the template parameter is deduced to `int` likewise. – Johannes Schaub - litb Feb 01 '10 at 08:55
  • @UncleBens: wow, thanks, I was totally unaware of the function template partial ordering scheme and all that. I've never stumbled upon it in real life. Good to know! – Potatoswatter Feb 01 '10 at 18:29
1

I stand corrected - see the comments below. I won't change any of my original post, as that would remove the context of the responses. I thank the commenters for their input, and for being so kind as to not vote me down


Consider templating to be like the macro pre-processor, which expands #defines before the compiler gets to see them.

The compiler will "expand" your template parameters and then look at your function declarations. So, template parameter == function parameter. If you declare the same function twice, you will get an error.

You ask about return type. That is part of the function's 'signature'. Two functions with the same parameters but different return types are two different functions.

Mawg says reinstate Monica
  • 38,334
  • 103
  • 306
  • 551
  • 5
    The return type of a nontemplate function is _not_ part of its signature (cf. http://stackoverflow.com/questions/290038/is-the-return-type-part-of-the-function-signature/290048#290048). – James McNellis Feb 01 '10 at 02:35
  • 1
    The comparison with preprocessor macros is rather flawed. There are special rules about overloads. If you have `template void foo(T);` and `void foo(int);` then a call like `foo(3);` will definitely resolve to the latter (non-templates are preferred over templates if they match exactly). – UncleBens Feb 01 '10 at 08:09
  • if the return type were part of the signiture you would be able to overload on retrun type, which you can't – jk. Feb 01 '10 at 09:50
  • It should be noted that a signature is not primarily used to determine whether something can be overloaded or not. The sentence in the definition of "signature" reading "the information about a function that participates in overload resolution" has been removed from c++0x, because it's misleading. Overloading is done on declarations, but not on the functions itself: `namespace A { void f(); } void f(); using A::f;` even tho they have different signatures, the two declarations of them cannot be overloaded. See also http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#357 – Johannes Schaub - litb Feb 01 '10 at 10:48
1

In addition to comments, some more information on topic in Herb Sutters's article Why Not Specialize Function Templates. Hope it would be helpful, too.

Alexander Poluektov
  • 7,844
  • 1
  • 28
  • 32
0
void fun(int i){
    cout<<i<<endl;
}

void fun(int i, string str){
    cout<<i<<str<<endl;
}

template <typename ... Args>
void sender(Args&& ... args)
{
    fun(forward<Args>(args) ...);
}

int main(){
    sender(5, "hello");
    sender(7);
}
RishiN
  • 39
  • 5