We can specialize a function or type template. The template that we specialize is called a primary template to differentiate it from the specialization that is also a template. A specialization template is called a specialization in short. A specialization overwrites the definition of its primary template. We cannot specialize further a specialization.
A primary template (of a function or type) defines the template name and its parameters: their number and their kinds. A specialization has to have the same name (of a function or type) and provide all arguments for the primary template.
A specialization can be partial or explicit. A specialization of a function template can only be explicit (it cannot be partial). A specialization of a type template can be either partial or explicit.
As opposed to the explicit specialization, the partial specialization allows to define template parameters that are used in the arguments of the primary template.
A function template can be specialized only explicitly, i.e., all arguments for the primary template are explicitly given: a explicit specialization template has no parameters (its parameter list is empty). Therefore a declaration and a definition of a function template specialization begin with the template header of an empty parameter list:
template <>
Then there follows the definition of the function template that looks
just like a definition of a regular (non-template) function, because
we do not use in it (i.e., in the function parameter list and the
function body) the parameter names of the primary template, but just
its arguments (e.g., int, 1 or std::list). But there is a
difference.
The difference is about the function name. In the specialization we put the argument list of the primary template after the function name, which we don’t do for a non-template function.
Here’s an example:
#include <iostream>
struct A
{
};
// The definition of the primary function template.
template <typename T>
void foo(const T &t)
{
std::cout << __PRETTY_FUNCTION__ << ": ";
std::cout << t << std::endl;
}
// A complete specialization of a function template.
template <>
void foo<A>(const A &)
{
std::cout << __PRETTY_FUNCTION__ << ": ";
std::cout << "A" << std::endl;
}
int
main()
{
foo(1);
foo(.2);
foo("Hello!");
foo(A());
}
We can also only declare a primary template or a specialization. If we declare a primary template and not define it later, then there is no definition of this function primary template. We can specialize this template and use it only for the specializations defined. This is shown by the example below.
We can skip the argument list for the primary template if the compiler
is able to figure it out (deduce it?) based on the function parameter
list. In the example below we skipped the argument list (<A>) of
the primary template after the function name foo in the declaration
and definition of the specialization.
#include <iostream>
struct A
{
};
// A declaration of the primary function template.
template <typename T>
void foo(const T &t);
// A declaration of a complete specialization of a function template.
// The compiler deduces the arguments of the primary template.
template <>
void foo(const A &);
// A definition of a complete specialization of a function template.
// The compiler deduces the arguments of the primary template.
template <>
void foo(const A &)
{
std::cout << __PRETTY_FUNCTION__ << ": ";
std::cout << "A" << std::endl;
}
int
main()
{
// foo(1);
foo(A());
}
We cannot partially specialize a function template. A partial specialization would define a template parameter, but this is not allowed:
#include <iostream>
template <typename T>
struct C
{
};
// A declaration of the primary function template.
template <typename T>
void foo(const T &t);
// Error: a partial specialization of a function template is not
// allowed.
// template <typename T>
// void foo<C<T>>(const C<T> &)
// {
// std::cout << __PRETTY_FUNCTION__ << ": ";
// std::cout << "C<T>" << std::endl;
// }
// This is another primary template. It's not a specialization.
template <typename T>
void foo(const C<T> &)
{
std::cout << __PRETTY_FUNCTION__ << ": ";
std::cout << "C<T>" << std::endl;
}
int
main()
{
// foo(1);
foo(C<int>());
}
An example below shows a recursive function template, where the
recursion is terminated by a template specialization. In the
specialization, we have to provide the argument 0 for parameter N,
because a compiler is unable to figure it out. However, we didn’t put
the argument int for the parameter T, because that a compiler can
figure out.
#include <iostream>
template <typename T>
void print(const T &t)
{
std::cout << t << '\n';
}
template <unsigned N, typename T>
void print(const T &t)
{
print(t);
print<N - 1>(t);
}
// Complete specialization of a function template.
template <>
void print<0>(const int &)
{
}
int
main()
{
print("Hello!");
print<10>(1);
}
Can we do without templates? Are overloads of regular functions not
enough? In the example below the foo function is overloaded in
order to use different implementations depending on the function
argument. The problem is that for every required type we either have
to implement an overload or use the implicit conversion, as in the
example below: for the argument '1' of type char there is called
the overload for type int.
#include <iostream>
struct A
{
};
void foo(const int &i)
{
std::cout << "Function overload: ";
std::cout << i << std::endl;
}
void foo(const A &)
{
std::cout << "Function overload: ";
std::cout << "A" << std::endl;
}
int
main()
{
foo(1);
foo('1');
foo(A());
}
In the above example, let’s replace the regular function overload for
const int & with a primary template, so that with a single
implementation we can deal with foo(1) and foo(‘1’). Therefore in
the example below we have a primary template for any type, and a
regular function for type A. Will the primary template or the
regular function be used when calling foo with an argument of type
A? A regular function always comes first.
#include <iostream>
struct A
{
};
// The definition of the primary function template.
template <typename T>
void foo(const T &t)
{
std::cout << __PRETTY_FUNCTION__ << ": ";
std::cout << t << std::endl;
}
// A function overload.
void foo(const A &)
{
std::cout << "Function overload: ";
std::cout << "A" << std::endl;
}
int
main()
{
foo(1);
foo('1');
foo(A());
}
For the primary function template, we can add a specialization for T
= A, but a compler uses the regular function anyway. During overload
resolution, a compiler does not consider specializations, but only the
overloads of regular functions and overloads of primary function
templates.
#include <iostream>
struct A
{
};
// The definition of the primary function template.
template <typename T>
void foo(const T &t)
{
std::cout << __PRETTY_FUNCTION__ << ": ";
std::cout << t << std::endl;
}
// A definition of a complete specialization of a function template.
template <>
void foo(const A &)
{
std::cout << __PRETTY_FUNCTION__ << ": ";
std::cout << "A" << std::endl;
}
// A function overload.
// void foo(const A &)
// {
// std::cout << "Function overload: ";
// std::cout << "A" << std::endl;
// }
int
main()
{
foo(1);
foo('1');
foo(A());
}
It seems that a template specialization is redundant, because we achieved similar functionality with regular function overloads. There is, however, a functionality of the specialization that we cannot achieve with regular function overloads.
A template specialization allows a user to provide some implementation
to the code that was already included as a header file, e.g., a
template library. A library declares a primary function template that
it requires, but the definition of a specialization or even of the
primary template leaves to the user. That’s how a header file
library.hpp can look like:
// Declaration of a primary function template.
template <typename T>
void foo(const T &t);
// This function template uses the above declaration. Without the
// declaration, this template wouldn't compile.
template <typename T>
void goo(const T &t)
{
foo(t);
}
And that’s how to use the library:
#include "library.hpp"
#include <iostream>
// A regular function won't cut it.
void
foo(const int &)
{
std::cout << "overload\n";
}
// We need a specialization of the primary template.
template <>
void
foo(const int &)
{
std::cout << "specialization\n";
}
int
main()
{
goo(1);
}
C++ is a one-pass compiler, and so if we declare a regular function
foo after the inclusion of our library, then the library function
goo doesn’t know it and cannot call it. However, function goo
knows and can use the primary function template foo, because it was
previously declared.
We could move the definition of the regular function foo before the
#include directive, so that function goo can call it, but it’s
best not to make that mess.
We can declare or define a type template, i.e., a template of a struct, class or union. Such a primary template we can specialize explicitly or partially. The primary template and its specialization only share the type name, while their interfaces (public members), implementations and their memory size can completely differ.
An example of a type specialization in the standard library is
std::vector<bool>, i.e., a specialization of std::vector for type
bool. This specialization has an interface similar to the interface
of the primary template, but its implementation is completely
different.
Below we define a primary template of type A with one member
function foo. For an explicit specialization for the double
argument, type A derives from std::pair, has the goo function,
but doesn’t have the foo function.
#include <iostream>
// Definition of a primary template.
template <typename T>
struct A
{
void
foo()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
// A complete specialization.
template<>
struct A<double>: std::pair<double, double>
{
void
goo()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int
main()
{
A<int> i;
i.foo();
A<float>().foo();
// Error: "foo" is in the primary template, which we've overwritten.
// A<double>().foo();
// But we can call "goo" alright.
A<double>().goo();
// We compare with the < operator inherited from std::pair.
std::cout << (A<double>() < A<double>()) << std::endl;
}
A specialization template defines anew its parameters, which do not have anything to do with the primary template parameters. The point is to explicitly provide (after the type name) the arguments for the primary template, which have to depend on (to use) the specialization parameters.
In the example below we declare a primary template of type A with a
parameter T of the type kind, and then we define two
specializations, both with a parameter T. Parameters T of these
three templates have nothing to do with each other, because they are
of local scope.
The first specialization defines the implementation of type A for
the case, where the argument of the primary template is std::vector.
We allow for any type of the vector element by using the parameter T
of the specialization.
The second specialization defines the implementation of type A for
the case, where the argument of the primary template is a type
template that can be instantiated with a single argument int.
In the main function, type A is used with the two specializations.
The last use case is the most interesting, which is commented out,
because it cannot compile: a compiler is unable to decide which
specialization to use.
#include <iostream>
#include <list>
#include <vector>
#include <tuple>
// Definition of a primary template.
template <typename T>
struct A;
// A partial specialization for a vector of elements of any type.
template <typename T>
struct A<std::vector<T>>
{
void
foo()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
// A partial specialization for any template type of the integer
// argument.
template <template<typename...> typename T>
struct A<T<int>>
{
void
goo()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int
main()
{
A<std::vector<bool>>().foo();
A<std::vector<double>>().foo();
A<std::list<int>>().goo();
A<std::tuple<int>>().goo();
// Ambiguous instantiation: the first or the second specialization?
// A<std::vector<int>>() a;
}
We can specialize a function template and a type template.
A specialization can be either partial or explicit.
A specialization allows to overwrite the implementation of a primary template.
Can a function template be partially specialized?
What does the overload resolution prefer: a template function or a regular function?
Must a specialization of a type template inherit from a primary template?