11

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.

user1494080
  • 2,064
  • 2
  • 17
  • 36
  • Can you please add more details on your experiments ? – Bertrand Oct 27 '16 at 12:07
  • What exactly do you mean by "primary template of a function template specialization" in the compiler terms? Do you have a link to a formal definition or is just a terminology you proposed for the purpose of this question? I can not twist my brain to see "template specialization" in a similar light as "is-a/inheritance" relation - each specialization is actually a template on its very own, just more specific than the "general" template with the same name. (the end result of the compilation will show multiple functions being defined, so this is stuff that must have sense only during compilation) – Adrian Colomitchi Oct 27 '16 at 12:19
  • @Bertrand Experiment is simple: Let each function print its number and add `int main() { f(0, 0); }`. The program outputs `(3)` and thus (3) is a specialization of (2), not of (1). – user1494080 Oct 27 '16 at 12:21
  • from my point of view (2) and (3) are both a specialization of (1). I don't really understand what you mean by "(3) is a specialization of (2), not of (1)" – Bertrand Oct 27 '16 at 12:23
  • 1
    "The program outputs (3) and thus (3) is a specialization of (2)," How did you draw this conclusion? The program outputs `(3)` because `f(int, int)` is a perfect match, that's true. But beat me if I understand how you deduced from "`f(int, int)` is a perfect match for (3)" that "(3) is a specialization of (2)". – Adrian Colomitchi Oct 27 '16 at 12:26
  • Actually i don't think there's a specialization hierarchy here. It's not inheritance ... You should think more in term of function signature. f(1,1) matches (3) signature so i think it's preferred to (2) and (1) – Bertrand Oct 27 '16 at 12:32
  • 1
    Function templates cannot be specialized partially, thus (2) can never be a specialization. The function template `f` is simply an overloaded function template. Template specializations do not participate in overload resolution. Thus, in a first step, the compiler has to decide if `f(0,0)` calls (1) or (2). After that, in a second step, it considers specializations. According to overload resolution rules, the compiler maps `f(0,0)` to primary template (2) in the first step. In the second step, the compiler considers specializations. As the program outputs `(3)`, (3) must specialize (2). – user1494080 Oct 27 '16 at 12:37
  • The first calls (1), the second calls (3), as expected. My question is really, well, what's written in my main question. :) How to determine primary templates... – user1494080 Oct 27 '16 at 12:42
  • I found this on the subject http://en.cppreference.com/w/cpp/language/template_argument_deduction – Bertrand Oct 27 '16 at 12:48
  • What paragraph are you referring to exactly? – user1494080 Oct 27 '16 at 12:52
  • The subject looks heavy but I think you can find a similar example in _Explicit instantiation_ – Bertrand Oct 27 '16 at 13:03
  • To people playing with this, [live example](http://coliru.stacked-crooked.com/a/ba4835ef93c33db1) may be useful. This demonstrates that (3) is not a specialization of (1), but in the absence of (2) it would be. – Yakk - Adam Nevraumont Oct 27 '16 at 13:11
  • @Yakk Is it ok if I include a link to your example in my answer? – Rerito Oct 27 '16 at 14:58
  • @Rerito Yes. And/Or just copy it. – Yakk - Adam Nevraumont Oct 27 '16 at 15:01

2 Answers2

10

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:

  • Transformed (1) applied to (2): Say foo with T = T1 and U=T2. It tries to match with (2): deduction fails
  • Transformed (2) applied to (1): foo(T1, T1), when applied to (1), it resolves as T = T1 and U = T1.

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.

Alexander Malakhov
  • 3,383
  • 2
  • 33
  • 58
Rerito
  • 5,886
  • 21
  • 47
  • Well, I even know why `foo(1, 1)` is matched to (2): because of the partial ordering rules of overloaded function templates. But yes, that is really not my question. – user1494080 Oct 27 '16 at 12:47
  • @user1494080 After reading more about it, it seems the same partial ordering technique is used to deduce which template to specialize – Rerito Oct 27 '16 at 13:03
  • "when [...] an explicit specialization refers to a function template specialization" seems on-point. – Yakk - Adam Nevraumont Oct 27 '16 at 13:13
  • 1
    Thank you very much, now I also found it in the C++ standard; it is written in section 14.5.6.2 [temp.func.order] of the C++11/14 standard. – user1494080 Oct 27 '16 at 13:19
0

The subject seems to be the template 'partial ordering'. An illustrated example can be found here: what's the partial ordering procedure in template deduction

Community
  • 1
  • 1
Bertrand
  • 994
  • 9
  • 23