Difference Between Function Overloading and Template Function Which Is More Appropriate

function overloading vs function templates - C++

Templates provide an advantage when you want to perform the same action on types that can be different. A simple example:

template <typename T>
T foo(const T& a, const T& b) { return a + b; }

You can use overloading when you want to apply different operations depending on the type:

struct Foo{ void foo() const {} };

void foo(int i) { std::cout << "i = " << i << "\n"; }
void foo(const Foo& f) { f.foo(); }

You could achieve the above using templates and template specializations, but such specializations should represent a few exceptions to the general case.

Differences between template specialization and overloading for functions?

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.

When to prefer plain function overload over templates (overload)?

One big difference between template specializafions and overloads is that overloads allow implicit conversions of arguments, while template specializations do not. Continuing your example, how will float argument be handled? func(double param) will be chosen, but the generic template will be instantiated. Often the former is correct.

More generally, there are reasons to prefer non-template code. Templates must usually reside in a header, which slows down compilation and exposes implementation to everybody. Templates cause more cryptic compilation error messages and are not supported by tools very well. E.g. Visual studio 2015 does not give intellisense info for templates.

Specialize Function Templates vs Function Overload vs Class Specializing

The problem with explicitly specialising a function template only applies if the function is also overloaded:

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

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

int main () {
int * i;
foo (i); // Calls #1 not specialization of #2
}

Without the #1 overload, the code will work as expected. However, a function that starts out not being overloaded may have overloads added as the code is maintained into the future.

This is one of those examples where, although I hate to say it, C++ has too many ways to do the same thing. Personally, if you find you need to specialise a function template, then I like the pattern suggested by TimW in his comment against Neil Butterworth's answer, ie. it's best to do so by having the current function dispatch it's call to a specialized class template instead:

template <typename T> class DoFoo {
static void do (T) { /* default behaviour */ }
};
template <> class DoFoo<int*> {
static void do (int*) { /* int * behaviour */ }
};

template <typename T> void foo (T t)
{
DoFoo<T>::do (t);
}

If 'foo' is overloaded, then at least it's clearer to the developer that this function won't be called, ie. the developer doesn't need to be a standards guru to know how the specialization rules interact with overload resolution.

Ultimately, however, the code generated by the compiler is going to be the same, this is purely a code comprehension issue on the part of the developer.

Template function specialization vs. overloading

The overload works fine for most of the contexts, and AFAIK is the suggested baseline approach. (see GOTW suggested by juanchopanza )

The difference hits if someone explicitly asks for the template, calling min<int>(x, y). In that case overloads are ignored and only the template (base or specialized) are considered.

Template Specialization VS Function Overloading

Short story: overload when you can, specialise when you need to.

Long story: C++ treats specialisation and overloads very differently. This is best explained with an example.

template <typename T> void foo(T);
template <typename T> void foo(T*); // overload of foo(T)
template <> void foo<int>(int*); // specialisation of foo(T*)

foo(new int); // calls foo<int>(int*);

Now let's swap the last two.

template <typename T> void foo(T);
template <> void foo<int*>(int*); // specialisation of foo(T)
template <typename T> void foo(T*); // overload of foo(T)

foo(new int); // calls foo(T*) !!!

The compiler does overload resolution before it even looks at specialisations. So, in both cases, overload resolution chooses foo(T*). However, only in the first case does it find foo<int*>(int*) because in the second case the int* specialisation is a specialisation of foo(T), not foo(T*).


You mentioned std::swap. This makes things even more complicated.

The standard says that you can add specialisations to the std namespace. Great, so you have some Foo type and it has a performant swap then you just specialise swap(Foo&, Foo&) in the std namespace. No problems.

But what if Foo is a template class? C++ doesn't have partial specialisation of functions, so you can't specialise swap. Your only choice is overloading, but the standard says that you aren't allowed to add overloads into the std namespace!

You have two options at this point:

  1. Create a swap(Foo<T>&, Foo<T>&) function in your own namespace, and hope that it gets found via ADL. I say "hope" because if the standard library calls swap like std::swap(a, b); then ADL simply won't work.

  2. Ignore the part of the standard that says not to add overloads and do it anyway. Honestly, even though it's technically not allowed, in all realistic scenarios it's going to work.

One thing to remember though is that there's no guarantee that the standard library uses swap at all. Most algorithms use std::iter_swap and in some implementations that I've looked at, it doesn't always forward to std::swap.

Class Template Specialization vs. Function Overloading

If the only thing that behaves differently is a single function, then you don't have to specialize the whole class, you can just specialize that function. I'm not sure if there's syntax to do this when the function is defined within the body of the class, but if you define the function externally, then you can do it like this:

template <class T>
class X
{
void f();
};

template <class T>
void X<T>::f()
{
// general code
}

template<>
void X<std::string>::f()
{
// specialized code
}

For multiple template parameters

template<int K, typename T> class X;
template<int K, typename T> void friend_func(X<K,T> &);

template<int K, typename T>
class X
{
public:
void class_func();
friend void friend_func<>(X &);
};

template<int K, typename T>
void X<K,T>::class_func()
{
friend_func(*this);
}

template<int K, typename T>
void friend_func(X<K,T> & x)
{
// non specialized version
}

template<int K>
void friend_func(X<K,std::string> & x)
{
// specialized version
}

Function template overloading vs Explicit specialization

Both are explicit specialization syntax

  • template <> double Sum(const map<string, double> &m);

  • template <> double Sum<map<string, double> >(const map<string, double> &m)

The first one let compiler deduce the parameter, whereas the second, you are explicit.

The second is required when compiler cannot deduced all template parameters as for

template <typename T> std::string getNameType();

template <> std::string getNameType<int>() { return "int"; }

or to disambiguate which template function to specialize

template <typename T> void foo(T);

template <typename T> void foo(T*); // overload, not specialization

//template <> void foo(int*); // Error: do you mean T = int for foo(T*) or T = int* for foo(T)

template <> void foo<int*>(int*); // specialize foo(T)
template <> void foo<int>(int*); // specialize foo(T*)

It is generally better to use overload instead of specialization for function, so for your example:

template <typename Key>
double Sum(const std::map<Key, double> &m)
{
double sum = 0;
for (const auto& p : m) { sum += p.second; } return sum;
}


Related Topics



Leave a reply



Submit