It is usually quite intuitive what the primary template of a function template specialization is, however, I am looking for the formal rules to understand the more surprising situations. For example:
template <typename T, typename U>
void f(T, U) {} // (1)
template <typename T>
void f(T, T) {} // (2)
template <>
void f(int, int) {} // (3); specializes (2), not (1); why?
Theoretically, (3) could also be a specialization of (1), but as experiments show it is not.
The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.
Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.
Class Templates like function templates, class templates are useful when a class defines something that is independent of the data type. Can be useful for classes like LinkedList, BinaryTree, Stack, Queue, Array, etc. Following is a simple example of a template Array class.
There are two types of templates in C++, function templates and class templates.
Let's focus on the declaration of the generic templates (1) and (2). These are two distinct templates, e.g. (2) is not a specialization of (1). Ok, now when we write a specialization:
template <>
void foo(int, int) {}
When deducing which template to specialize, the compiler will identify two candidates. Then, it must chose which is the best fit. The process for such a choice is called "partial ordering of function templates". Chosen quote:
When the same function template specialization matches more than one overloaded function template (this often results from template argument deduction), partial ordering of overloaded function templates is performed to select the best match.
Let's call S the set of matching templates. Then, for each pair (f1, f2) in S, the compiler will transform f1 by applying dummy types (resp. values) on its type (resp. non type) parameters. Then it tries to match it against f2. Then it does the same procedure by transforming f2 and trying to match it against f1. At the end, after going through each pair, the compiler can determine which template candidate is the most specialized. If it fails to do so the compilation fails.
In our case we've got two matching templates so we apply the procedure described above:
From this procedure, the compiler deduces that (2) is more specialized than (1) and your specialization goes for (2). The same process is applied during overload resolution when the compiler focuses on a particular call.
An example illustrating all this procedure is the following (taken from @Yakk's comment):
template <typename T, typename U>
void f(T, U) { std::cout << "f(1)\n"; } // f(1)
template <typename T>
void f(T, T) { std::cout << "f(2)\n"; } // f(2)
template <>
void f(int, int) { std::cout << "f(3)\n"; } // f(3); specializes f(2), not f(1); why?
// Now the same specialization but without any template overload...
template <typename T, typename U>
void g(T, U) { std::cout << "g(1)\n"; } // g(1)
template <>
void g(int, int) { std::cout << "g(3)\n"; } // g(3); No ambiguity, specializes g(1)
Next, let's perform a few calls:
f(1, 1); // Prints f(3)
f<int>(1, 1); // Prints f(3)
f<int, int>(1, 1); // Prints f(1)
f(0.1, 0.2); // Prints f(2)
g(1, 1); // Prints g(3)
g<int, int>(1, 1); // Prints g(3)
All this can be seen in action here - copied from @Yakk's comment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With