How to define a primary function specialization pattern?

It is usually intuitively clear that the main template is the specialization of the function template, however, I am looking for formal rules for understanding more unexpected situations. For instance:

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) can also be a specialization of (1), but, as experiments show, this is not so.
+6
source share
2 answers

Focus on declaring common patterns (1) and (2). These are two different patterns, for example. (2) is not a specialization (1). Ok, now that we write the specialization:

 template <> void foo(int, int) {} 

When inferring which template should be specified, the compiler identifies two candidates. Then he must choose which one is best suited. The process for making this selection is called the "partial order of function templates . " Selected Quote:

When the same function template specification corresponds to more than one overloaded function template (this often occurs due to the deduction of the template argument), the overloaded function templates are partially ordered to select the best match.

Let S invoke a set of matching patterns. Then, for each pair (f1, f2) into S, the compiler converts f1, using dummy types (corresponding values) for parameters of its type (respectively, not type). Then he tries to match it with f2. He then performs the same procedure, converting f2 and trying to match it with f1. In the end, after passing each pair, the compiler can determine which template candidate is the most specialized. If this fails, compilation will fail.

In our case, we have two suitable templates, so we use the procedure described above:

  • Transformation (1) applies to (2): Say foo with T = T1 and U = T2. He tries to match (2): the output is not executed.
  • Transformation (2) applies to (1): foo (T1, T1), when applied to (1), it is allowed as T = T1 and U = T1.

From this procedure, the compiler deduces that (2) is more specialized than (1), and your specialization goes to (2). The same process applies during overload resolution, when the compiler focuses on a specific call.

An example illustrating this entire procedure is as follows (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 me make a few calls:

 f(1, 1); // Prints f(3) f<int>(1, 1); // Prints f(3) f<int, int>(1, 1); // Prints f(1) g(1, 1); // Prints g(3) g<int, int>(1, 1); // Prints g(3) 

All of this can be seen in action here - copied from @Yakk's comment .

+8
source

The subject seems to be a "partial ordering" of the template. An illustrated example can be found here: what is the partial order procedure in the template output

0
source

Source: https://habr.com/ru/post/1011763/


All Articles