74

A textbook I have notes that you can provide your own implementation for standard library functions like swap(x,y) via template specialization or function overloading. This would be useful for any types which can benefit from something other than an assignment swap, like STL containers for example (which already have swaps written, I know).

My questions are the following:

  1. What's better: template specialization to give your specialized swap implementation, or function overloading providing the exact parameters you wish to use without a template?

  2. Why is it better? Or if they're equal, why is this?

Enlico
  • 23,259
  • 6
  • 48
  • 102
John Humphreys
  • 37,047
  • 37
  • 155
  • 255

3 Answers3

105

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.

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
  • 8
    "I say hope because" ... This point was noted in WG21 several years ago. All standard library implementors know not to. – MSalters Aug 18 '11 at 14:08
  • 1
    Simple clear explanation: Both case the compiler find best match overloading F(T*) first. But in case 1 it can find a specialization for F(T*) - foo(int*), so it choose that. In case 2, there isn't a specialization for F(T*), so it choose F(T*). Note in case 2 the only specialization is for F(T), NOT F(T*). – RoundPi Oct 02 '12 at 17:30
  • 5
    Why so many up votes? I tested in gcc 4.8 and LLVM 5.0, foo(T*) is chosen for the first example. – Cosyn Oct 29 '13 at 05:19
  • 1
    _"I say "hope" because if the standard library calls swap like `std::swap(a, b);` then ADL simply won't work."_ The standard guarantees the library will not do that, so it simply will work, and so option (1) is always better than (2). – Jonathan Wakely Sep 15 '15 at 10:48
  • If I call foo(new double), it will call the base function rather than the overload. Can someone explain why? – Andrew Sep 29 '17 at 01:24
  • @Andrew, because function specialisations don't participate in the ADL. – Adrian Oct 05 '17 at 20:19
  • Why does the standard allow for function specializations to be added to std? Is that an omission? Or is it because function template specialization is just not that useful? – Adrian Oct 05 '17 at 20:21
  • Did you mean "_only in the first case does it find `foo(int*)`_" (i.e. `foo` instead of `foo`)? – Ruslan Mar 05 '18 at 11:42
  • 3
    "Now let's swap the last two". The last 2 lines were NOT the same. One is "void foo(int*);" and another is "void foo(int*);" – SridharKritha Mar 04 '20 at 07:00
18

There's little to add to Peter Alexander's answer. Let me just mention one use in wich function specialization could be prefearable over overloading: if you have to select among functions with no parameters.

E.g.

template<class T> T zero();
template<> int zero() { return 0; }
template<> long zero() { return 0L; }

To do something similar using function overloading, you would have to add a parameter to the function signature:

int zero(int) { return 0; }
long zero(long) { return 0L; }
Paolo M
  • 12,403
  • 6
  • 52
  • 73
  • 1
    You might use `template struct Tag{};` for tag dispatching as dummy parameter. that handle types that you cannot construct or pass easily as parameter: `foo(tag{})`, `foo(tag{})`. – Jarod42 Sep 19 '19 at 20:10
8

You aren't allowed to overload functions in the std namespace, but you are allowed to specialize templates (as I recall), so that's one option.

The other option is to put your swap function in the same namespace as the thing it's operating on and using std::swap; before calling an unqualified swap.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • I prefer `using std::swap;` to `using namespace std;`. – Steve Jessop Aug 18 '11 at 13:27
  • You may specialize some templates: _"A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited."_ [n3337; 17.6.4.2.1/1] So you may specialize `std::swap` for types not in `std`. – boycy Feb 20 '14 at 13:35
  • why you can not overload std functions? is bad habit but std namespace is not special. I do not like it, but many products add std swap for their types in std namespace. – Nick Sep 04 '16 at 16:03