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>
#include <string>
// The definition of the primary function template.
template <typename T>
void foo(const T &t)
{
std::cout << __PRETTY_FUNCTION__ << ": " << t << std::endl;
}
// An explicit specialization of a function template. The argument
// for the primary template is explicitly given.
template <>
void foo<std::string>(const std::string &)
{
std::cout << "A template specialization for std::string.\n";
}
int main()
{
foo(1);
foo(.2);
foo("Hello!");
foo(std::string());
}
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 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>
#include <string>
// A declaration of the primary function template.
template <typename T>
void foo(const T &t);
// Specialization #1: the argument of the primary template deduced.
template <>
void foo(const int &);
// Specialization #2: the argument of the primary template deduced.
template <>
void foo(const std::string &)
{
std::cout << "A template specialization for std::string.\n";
}
// Specialization #3: the deduction fails.
// template <>
// void foo(bool);
int main()
{
// foo(1); // Linking fails.
// foo(.2); // Linking fails.
foo(std::string());
}
We cannot partially specialize a function template. A partial specialization would define a template parameter, but this is not allowed:
#include <array>
#include <iostream>
// A declaration of the primary function template.
template <typename T>
void foo(const T &t);
// Error: a partial specialization of a function template not allowed.
// template <typename T, std::size_t I>
// void foo<std::array<T, I>>(const std::array<T, I> &)
// {
// std::cout << "A template specialization: "
// << __PRETTY_FUNCTION__ << std::endl;
// }
// This is another primary template, not a specialization. A
// specialization could have an explicit argument for the primary
// template. We can argue that we removed it, so that a compiler can
// deduce it, but then this template becomes primary.
template <typename T, std::size_t I>
void foo(const std::array<T, I> &)
{
std::cout << "A template overload: "
<< __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
foo(std::array{1, 2, 3});
}
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);
}
// An explicit 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>
#include <string>
void foo(const int &i)
{
std::cout << "Function overload: " << i << std::endl;
}
void foo(const std::string &)
{
std::cout << "Function overload for std::string.\n";
}
int main()
{
foo(1);
foo('1');
foo(std::string());
}
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>
#include <string>
// A definition of a primary function template.
template <typename T>
void foo(const T &t)
{
std::cout << __PRETTY_FUNCTION__ << ": " << t << std::endl;
}
// A function overload.
void foo(const std::string &)
{
std::cout << "Function overload for std::string.\n";
}
int main()
{
foo(1);
foo('1');
foo(std::string());
}
For the primary function template, we can add a specialization for T
= A, but a compiler 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>
#include <string>
// A definition of a primary function template.
template <typename T>
void foo(const T &t)
{
std::cout << __PRETTY_FUNCTION__ << ": " << t << std::endl;
}
// A function overload.
void foo(const std::string &)
{
std::cout << "Function overload for std::string.\n";
}
// A definition of an explicit specialization of a function template.
template <>
void foo(const std::string &)
{
std::cout << "Template specialization for std::string.\n";
}
int main()
{
foo(1);
foo('1');
foo(std::string());
}
It seems that a specialization of a primary function template is redundant, because a regular function overload offers a similar (and even better, because it comes first) functionality: both a specialization (that is only explicit) and an overload are (defined, intended) for a specific type of a function argument. There is, however, a functionality of the specialization that we cannot achieve with a regular function overload.
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;
}
};
// An explicit 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 as neither of them is more specialized than the
other.
#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?