Question

I read C++ Primer, and it says function template specialization is an advanced topic, but I am totally lost. Can anybody offer an example why function template specialization is important and necessary?

Why don't function templates support partial specialization while class templates do? What's the underlying logic?

Was it helpful?

Solution

Basically the idea is that you can write templates that behave in a generic way for the general case, but can still handle special cases. One example of where specialization is used is in std::vector. std::vector<bool> is a specialization that packs the bool elements such that they only use one bit per element, not one byte. std::vector<T> works like a normal dynamic array for all other types.

The more advanced use for specialization is metaprogramming. For example, here's an example (from Wikipedia) of how to use template specialization to compute factorials at compile time.

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

OTHER TIPS

Your question of why functions do not support partial specialization can be answered here. The code below shows how to implement the different specializations.

template<typename T>
bool Less(T a, T b)
{
    cout << "version 1 ";
    return a < b;
}
// Function templates can't be partially specialized they can overload instead.
template<typename T>
bool Less(T* a, T* b)
{
    cout << "version 2 ";
    return *a < *b;
}

template<>
bool Less<>(const char* lhs, const char* rhs)
{
    cout << "version 3 ";
    return strcmp(lhs, rhs) < 0;
}

int a = 5, b = 6;

cout << Less<int>(a, b) << endl;
cout << Less<int>(&a, &b) << endl;
cout << Less("abc", "def") << endl;

I cannot think of an example, and I've been trying nearly since you asked. As pointed out by Jagannath, it's been long-standing advice not to specialize functions, but instead overload them or use a traits class (which can be specialized, even partially specialized).

For example, if you need to swap two items, then relying on overloads is better (more predictable and more extensible):

template<class T>
void f() {
  T a, b;
  using std::swap; // brings std::swap into scope as "fallback"
  swap(a, b); // unqualified call (no "std::") so ADL kicks in
  // also look at boost::swap
}

And how you write a swap for your types:

// the cleanest way to do it for a class template:
template<class T>
struct Ex1 {
  friend void swap(Ex1& a, Ex1& b) { /* do stuff */ }
};

// you can certainly place it outside of the class instead---but in the
// same namespace as the class---if you have some coding convention
// against friends (which is common, but misguided, IMHO):
struct Ex2 {};
void swap(Ex2& a, Ex2& b) { /* do stuff */ }

Both of which allow Argument Dependent Lookup (ADL).

Other functions, such as a stringify/str or a repr (representation) can similarly be non-members and take advantage of ADL through overloading:

struct Ex3 {
  friend std::string repr(Ex3 const&) { return "<Ex3 obj>"; }
};

std::string repr(bool b) { return b ? "true" : "false"; }

// possible fallback:
template<class T>
std::string repr(T const& v) {
  std::ostringstream out;
  out << v;
  return out.str();
}
// but in this particular case, I'd remove the fallback and document that
// repr() must be overloaded appropriately before it can be used with a
// particular type; for other operations a default fallback makes sense

To look at it another way, it would be nice if function templates could serve as a registry for specific implementations, but due to limits (in current C++, not sure exactly what C++0x brings here) they don't work as well as either overloading or class templates for that registry purpose.

There is one use that is convenient but not important: easily defining certain specializations to be in a separate library, possibly a shared library (.so or .dll). This is convenient because it requires minimal changes to the generic template, but not important because it seems rare to me (in the wild, and certainly is rare in my experience) and implementors can still use either overloading or forwarding to a fully-specialized class template's unspecialized method.

To illustrate why function template specialization is important, consider the std::swap template function. By default, std::swap(x, y) essentially does:

T temp = x;
x = y;
y = temp;

but this can be inefficient since it involves creating an extra copy of x and could do additional copying in the assignments. This is especially bad if x is large (for example, if it's a std::vector with many elements). Additionally, each of the above lines could fail and throw exceptions, potentially leaving x and y in bad, inconsistent states.

To address this, many classes provide their own swap methods (including std::vector) that instead swap the pointers to their internal data. This is more efficient and can be guaranteed to never fail.

But now you have a case where you can use std::swap(x, y) on some types but need to call x.swap(y) on other types. This is confusing, and it's bad for templates since they wouldn't be able to swap two objects in a generic, consistent way.

But std::swap can be specialized so that it calls x.swap(y) when called on specific types. That means you then can use std::swap everywhere and (hopefully) expect it to be well-behaved.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top