I'm having difficulty understanding the how formal ordering rules work as described in chapter 12 of the book C++ Templates, The Complete Guide by D. Vandevoorde and N. M. Josuttis. On page 188 of this book the authors give the following scenario used to decide which of two viable function templates is more specialized:
From these two templates we synthesize two lists of argument types by replacing the template parameters as described earlier: (A1) and (A2*) (where A1 and A2 are unique made up types). Clearly, deduction of the first template against the second list of argument types succeeds by substituting A2* for T. However, there is no way to make T* of the second template match the nonpointer type A1 in the first list. Hence, we formally conclude that the second template is more specialized than the first.
I'd like some understanding this example.
Edit
I believe that the two function templates referred to in the above quote are
template<typename T>
int f(T)
{
return 1;
}
template<typename T>
int f(T*)
{
return 2;
}
The rules are a bit harder to explain than to use. The idea is that a template is more specialized than another template if the set of possible instantiations of the more specialized is a strict subset of the set of possible instantiations of the less specialized one.
That is, every type that can be use as an argument to the more specialized can also be used as argument to the less specialized, and there is at least one type that can be used with the less specialized that cannot be used with the more specialized.
Given the two templates:
template <typename A> void f( A ); // [1]
template <typename B> void f( B* ); // [2]
The question to resolve is which one of them is more generic (i.e. can take a greater number of arguments). The whole description in the standard is done in terms of synthetic unique types that are used for A and B, but in a less precise way we can try to resolve by hand waving.
Say we find a type X that matches the second template argument, then an instantiation of that second template will look like void f( X* ) (besides the fact that it is a template). Now, can the template [1] be used to generate an equivalent function? Yes, by making A == X* in the type deduction. Can we do it in the opposite direction? Say we find a type Y with which we can instantiate the first template, we get void f( Y ). Can the second template match this call? No, only for the subset of types that are pointers the previous statement can hold.
That means that the second template is more specialized, since for every valid instantiation of the second template, we can also instantiate the first template, but there are some instantiations of the first template that would not be valid instantiations of the second.
For a practical example, f( char* ) can be matched by both templates, but f( 5 ) can only be matched by the first one. The reason for the weird explanation in terms of synthetic types is that a single example does not guarantee the order, it has to hold for all types. The synthetic type is a representative of any type.
I think the point of the authors is that T in the first template can be matched to both a type A and a pointer to a type A*, while the second template can only be matched to a pointer to a type A*, therefore the second template is more specialized.
Related
Recently I reported a msvc bug involving a function parameter pack. Also as it turns out here that msvc is actually standard compliant there.
Then when I modified the example to what is shown below I noticed that the modified code also cannot be compiled in msvc but can be compiled in clang and gcc. The code is as follows: Demo link
template<typename T> struct C{};
template<typename T> void f(C<T>)
{
}
template<typename... T> void f(C<T...>)
{
}
int main()
{
f(C<int>{}); //Should this call succeed?
}
Note that in the above example we have as the function parameter C<T...> as opposed to just T.... Now, in the above shown example, I am not 100% sure that if it is a msvc issue or the standard disallows the program.
So my question is, is the above shown code example well formed. That is, should the call f(C<int>{}); succeed choosing the first overload void f(C<T>) instead of void f(C<T...>)?
In other words, which compiler is right here.
So my question is, is the above shown code example well formed.
The code is well-formed as the function template overload with a non-variadic template parameter is more specialized than the overload with a variadic template parameter, by partial ordering rules.
MSVC is incorrect to reject it.
[temp.func.order]/1 through /4 tells us that we need to turn to partial ordering
/1 If a function template is overloaded, the use of a function template
specialization might be ambiguous [...]. Partial ordering of overloaded function template declarations is used in the following contexts to select the function template to which a function template specialization refers:
/1.1 during overload resolution for a call to a function template specialization ([over.match.best]);
[...]
/2 Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. [...] If so, the more specialized template is the one chosen by the partial ordering process. If both deductions succeed, the partial ordering selects the more constrained template (if one exists) as determined below.
/3 To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs
thereof) synthesize a unique type, value, or class template
respectively and substitute it for each occurrence of that parameter
in the function type of the template.
/4 Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial].
[temp.deduct.partial]
/2 Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type
and the transformed function type. The deduction process uses the
transformed type as the argument template and the original type of the
other template as the parameter template. [...]
/4 Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of
P and A.
/8 Using the resulting types P and A, the deduction is then done as described in [temp.deduct.type]. [...] If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.
results in, for the original single function parameter function templates, the following P/A pairs:
P A
-------- ----------
#1: C<T> C<Unique1...>
#2: C<T...> C<Unique2>
Typically, when not in the context of partial ordering, deduction would succeed for both these pairs. However, [temp.deduct.type]/9 has a special case for when the deduction is performed as part of partial ordering:
/9 [...] During partial ordering, if Ai was originally a pack expansion:
/9.2 otherwise, if Pi is not a pack expansion, template argument deduction fails.
This clause means deduction of #1 above (C<T> from C<Unique1...>) fails, whereas #2 (C<T...> from C<Unique2>) succeeds and C<Unique2> is considered at least as specialized as C<T...>. As per [temp.deduct.partial]/10 the non-variadic function template overload is thus at least as specialized as the variadic template function overload and, in the absense of the vice-versa relationsship, moreover more specialized:
/10 Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering,
the type from F is at least as specialized as the type from G. F is
more specialized than G if F is at least as specialized as G and G is
not at least as specialized as F.
This leads us back to [temp.func.order]/2 and the more specialized function template is chosen by partial ordering:
/2 [...] The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.
Which is also covered by [over.match.best]/2.5
/2 Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments
i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
[...]
/2.5 F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2
according to the partial ordering rules described in
[temp.func.order], or, if not that, [...]
Last week Eric Niebler tweeted a very compact implementation for the std::is_function traits class:
#include <type_traits>
template<int I> struct priority_tag : priority_tag<I - 1> {};
template<> struct priority_tag<0> {};
// Function types here:
template<typename T>
char(&is_function_impl_(priority_tag<0>))[1];
// Array types here:
template<typename T, typename = decltype((*(T*)0)[0])>
char(&is_function_impl_(priority_tag<1>))[2];
// Anything that can be returned from a function here (including
// void and reference types):
template<typename T, typename = T(*)()>
char(&is_function_impl_(priority_tag<2>))[3];
// Classes and unions (including abstract types) here:
template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];
template <typename T>
struct is_function
: std::integral_constant<bool, sizeof(is_function_impl_<T>(priority_tag<3>{})) == 1>
{};
But how does it work?
The general idea
Instead of listing all the valid function types, like the sample implementation over on cpprefereence.com, this implementation lists all of the types that are not functions, and then only resolves to true if none of those is matched.
The list of non-function types consists of (from bottom to top):
Classes and unions (including abstract types)
Anything that can be returned from a function (including void and reference types)
Array types
A type that does not match any of those non-function types is a function type. Note that std::is_function explicitly considers callable types like lambdas or classes with a function call operator as not being functions.
is_function_impl_
We provide one overload of the is_function_impl function for each of the possible non-function types. The function declarations can be a bit hard to parse, so let's break it down for the example of the classes and unions case:
template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];
This line declares a function template is_function_impl_ that takes a single argument of type priority_tag<3> and returns a reference to an array of 4 chars. As is customary since the ancient days of C, the declaration syntax gets horribly convoluted by the presence of array types.
This function template takes two template arguments. The first is just an unconstrained T, but the second is a pointer to a member of T of type int. The int part here does not really matter, ie. this will even work for Ts that do not have any members of type int. What it does though is that it will result in a syntax error for Ts that are not of class or union type. For those other types, attempting to instantiate the function template will result in a substitution failure.
Similar tricks are used for the priority_tag<2> and priority_tag<1> overloads, which use their second template arguments to form expressions that only compile for Ts being valid function return types or array types respectively. Only the priority_tag<0> overload does not have such a constraining second template parameter and thus can be instantiated with any T.
All in all we declare four different overloads for is_function_impl_, which differ by their input argument and return type. Each of them takes a different priority_tag type as argument and returns a reference to a char array of different unique size.
Tag dispatching in is_function
Now, when instantiating is_function, it instantiates is_function_impl with T. Note that since we provided four different overloads for this function, overload resolution has to take place here. And since all of these overloads are function templates, that means SFINAE has a chance to kick in.
So for functions (and only functions) all of the overloads will fail except the most general one with priority_tag<0>. So why doesn't instantiation always resolve to that overload, if it's the most general one? Because of the input arguments of our overloaded functions.
Note that priority_tag is constructed in such a way that priority_tag<N+1> publicly inherits from priority_tag<N>. Now, since is_function_impl is invoked here with priority_tag<3>, that overload is a better match than the others for overload resolution, so it will be tried first. Only if that fails due to a substitution error the next-best match is tried, which is the priority_tag<2> overload. We continue in this way until we either find an overload that can be instantiated or we reach priority_tag<0>, which is not constrained and will always work. Since all of the non-function types are covered by the higher prio overloads, this can only happen for function types.
Evaluating the result
We now inspect the size of the type returned by the call to is_function_impl_ to evaluate the result. Remember that each overload returns a reference to a char array of different size. We can therefore use sizeof to check which overload was selected and only set the result to true if we reached the priority_tag<0> overload.
Known Bugs
Johannes Schaub found a bug in the implementation. An array of incomplete class type will be incorrectly classified as a function. This is because the current detection mechanism for array types does not work with incomplete types.
Consider the following simple (to the extent that template questions ever are) example:
#include <iostream>
template <typename T>
struct identity;
template <>
struct identity<int> {
using type = int;
};
template<typename T> void bar(T, T ) { std::cout << "a\n"; }
template<typename T> void bar(T, typename identity<T>::type) { std::cout << "b\n"; }
int main ()
{
bar(0, 0);
}
Both clang and gcc print "a" there. According to the rules in [temp.deduct.partial] and [temp.func.order], to determine partial ordering, we need to synthesize some unique types. So we have two attempts at deduction:
+---+-------------------------------+-------------------------------------------+
| | Parameters | Arguments |
+---+-------------------------------+-------------------------------------------+
| a | T, typename identity<T>::type | UniqueA, UniqueA |
| b | T, T | UniqueB, typename identity<UniqueB>::type |
+---+-------------------------------+-------------------------------------------+
For deduction on "b", according to Richard Corden's answer, the expression typename identity<UniqueB>::type is treated as a type and is not evaluated. That is, this will be synthesized as if it were:
+---+-------------------------------+--------------------+
| | Parameters | Arguments |
+---+-------------------------------+--------------------+
| a | T, typename identity<T>::type | UniqueA, UniqueA |
| b | T, T | UniqueB, UniqueB_2 |
+---+-------------------------------+--------------------+
It's clear that deduction on "b" fails. Those are two different types so you cannot deduce T to both of them.
However, it seems to me that the deduction on A should fail. For the first argument, you'd match T == UniqueA. The second argument is a non-deduced context - so wouldn't that deduction succeed iff UniqueA were convertible to identity<UniqueA>::type? The latter is a substitution failure, so I don't see how this deduction could succeed either.
How and why do gcc and clang prefer the "a" overload in this scenario?
As discussed in the comments, I believe there are several aspects of the function template partial ordering algorithm that are unclear or not specified at all in the standard, and this shows in your example.
To make things even more interesting, MSVC (I tested 12 and 14) rejects the call as ambiguous. I don't think there's anything in the standard to conclusively prove which compiler is right, but I think I might have a clue about where the difference comes from; there's a note about that below.
Your question (and this one) challenged me to do some more investigation into how things work. I decided to write this answer not because I consider it authoritative, but rather to organize the information I have found in one place (it wouldn't fit in comments). I hope it will be useful.
First, the proposed resolution for issue 1391. We discussed it extensively in comments and chat. I think that, while it does provide some clarification, it also introduces some issues. It changes [14.8.2.4p4] to (new text in bold):
Each type nominated above from the parameter template and the
corresponding type from the argument template are used as the types of
P and A. If a particular P contains no template-parameters that
participate in template argument deduction, that P is not used to
determine the ordering.
Not a good idea in my opinion, for several reasons:
If P is non-dependent, it doesn't contain any template parameters at all, so it doesn't contain any that participate in argument deduction either, which would make the bold statement apply to it. However, that would make template<class T> f(T, int) and template<class T, class U> f(T, U) unordered, which doesn't make sense. This is arguably a matter of interpretation of the wording, but it could cause confusion.
It messes with the notion of used to determine the ordering, which affects [14.8.2.4p11]. This makes template<class T> void f(T) and template<class T> void f(typename A<T>::a) unordered (deduction succeeds from first to second, because T is not used in a type used for partial ordering according to the new rule, so it can remain without a value). Currently, all compilers I've tested report the second as more specialized.
It would make #2 more specialized than #1 in the following example:
#include <iostream>
template<class T> struct A { using a = T; };
struct D { };
template<class T> struct B { B() = default; B(D) { } };
template<class T> struct C { C() = default; C(D) { } };
template<class T> void f(T, B<T>) { std::cout << "#1\n"; } // #1
template<class T> void f(T, C<typename A<T>::a>) { std::cout << "#2\n"; } // #2
int main()
{
f<int>(1, D());
}
(#2's second parameter is not used for partial ordering, so deduction succeeds from #1 to #2 but not the other way around). Currently, the call is ambiguous, and should arguably remain so.
After looking at Clang's implementation of the partial ordering algorithm, here's how I think the standard text could be changed to reflect what actually happens.
Leave [p4] as it is and add the following between [p8] and [p9]:
For a P / A pair:
If P is non-dependent, deduction is considered successful if and only if P and A are the same type.
Substitution of deduced template parameters into the non-deduced contexts appearing in P is not performed and does not affect the outcome of the deduction process.
If template argument values are successfully deduced for all template parameters of P except the ones that appear only in non-deduced contexts, then deduction is considered successful (even if some parameters used in P remain without a value at the end of the deduction process for that particular P / A pair).
Notes:
About the second bullet point: [14.8.2.5p1] talks about finding template argument values that will make P, after substitution of the deduced values (call it the deduced A), compatible with A. This could cause confusion about what actually happens during partial ordering; there's no substitution going on.
MSVC doesn't seem to implement the third bullet point in some cases. See the next section for details.
The second and third bullet points are intented to also cover cases where P has forms like A<T, typename U::b>, which aren't covered by the wording in issue 1391.
Change the current [p10] to:
Function template F is at least as specialized as function template
G if and only if:
for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G, and,
when performing deduction using the transformed F as the argument template and G as the parameter template, after deduction is done
for all pairs of types, all template parameters used in the types from
G that are used to determine the ordering have values, and those
values are consistent across all pairs of types.
F is more specialized than G if F is at least as specialized
as G and G is not at least as specialized as F.
Make the entire current [p11] a note.
(The note added by the resolution of 1391 to [14.8.2.5p4] needs to be adjusted as well - it's fine for [14.8.2.1], but not for [14.8.2.4].)
For MSVC, in some cases, it looks like all template parameters in P need to receive values during deduction for that specific P / A pair in order for deduction to succeed from A to P. I think this could be what causes implementation divergence in your example and others, but I've seen at least one case where the above doesn't seem to apply, so I'm not sure what to believe.
Another example where the statement above does seem to apply: changing template<typename T> void bar(T, T) to template<typename T, typename U> void bar(T, U) in your example swaps results around: the call is ambiguous in Clang and GCC, but resolves to b in MSVC.
One example where it doesn't:
#include <iostream>
template<class T> struct A { using a = T; };
template<class, class> struct B { };
template<class T, class U> void f(B<U, T>) { std::cout << "#1\n"; }
template<class T, class U> void f(B<U, typename A<T>::a>) { std::cout << "#2\n"; }
int main()
{
f<int>(B<int, int>());
}
This selects #2 in Clang and GCC, as expected, but MSVC rejects the call as ambiguous; no idea why.
The partial ordering algorithm as described in the standard speaks of synthesizing a unique type, value, or class template in order to generate the arguments. Clang manages that by... not synthesizing anything. It just uses the original forms of the dependent types (as declared) and matches them both ways. This makes sense, as substituting the synthesized types doesn't add any new information. It can't change the forms of the A types, since there's generally no way to tell what concrete types the substituted forms could resolve to. The synthesized types are unknown, which makes them pretty similar to template parameters.
When encountering a P that is a non-deduced context, Clang's template argument deduction algorithm simply skips it, by returning "success" for that particular step. This happens not only during partial ordering, but for all types of deductions, and not just at the top level in a function parameter list, but recursively whenever a non-deduced context is encountered in the form of a compound type. For some reason, I found that surprising the first time I saw it. Thinking about it, it does, of course, make sense, and is according to the standard ([...] does not participate in type deduction [...] in [14.8.2.5p4]).
This is consistent with Richard Corden's comments to his answer, but I had to actually see the compiler code to understand all the implications (not a fault of his answer, but rather of my own - programmer thinking in code and all that).
I've included some more information about Clang's implementation in this answer.
I believe the key is with the following statement:
The second argument is a non-deduced context - so wouldn't that deduction succeed iff UniqueA were convertible to identity::type?
Type deduction does not perform checking of "conversions". Those checks take place using the real explicit and deduced arguments as part of overload resolution.
This is my summary of the steps that are taken to select the function template to call (all references taken from N3937, ~ C++ '14):
Explicit arguments are replaced and the resulting function type checked that it is valid. (14.8.2/2)
Type deduction is performed and the resulting deduced arguments are replaced. Again the resulting type must be valid. (14.8.2/5)
The function templates that succeeded in Steps 1 and 2 are specialized and included in the overload set for overload resolution. (14.8.3/1)
Conversion sequences are compared by overload resolution. (13.3.3)
If the conversion sequences of two function specializations are not 'better' the partial ordering algorithm is used to find the more specialized function template. (13.3.3)
The partial ordering algorithm checks only that type deduction succeeds. (14.5.6.2/2)
The compiler already knows by step 4 that both specializations can be called when the real arguments are used. Steps 5 and 6 are being used to determine which of the functions is more specialized.
In this article, they are saying (c) explicit specialization of (b). My doubt is why can't we say it is explicit specialization of (a) ? because we can specialize the template for any particular type. so while specializing for int*, why they say (c) explicit specialization of (b) .
template<class T> // (a) a base template
void f( T );
template<class T> // (b) a second base template, overloads (a)
void f( T* ); // (function templates can't be partially
// specialized; they overload instead)
template<> // (c) explicit specialization of (b)
void f<>(int*);
Any comments will be helpful to understand the things.
If (b) didn't exist, then (c) would indeed be a valid specialisation of (a). In fact, just changing the order of the source lines so that (c) appears before the compiler has seen (b) will make it a specialisation of (a)!
Consider the following code:
int main()
{
int a;
f(&a);
return 0;
}
Now put yourself in the compiler's shoes. You need to find a matching function f with an int* argument. What do you do?
First, you try all the non-template functions called f that you know about, and see if there are any which match the argument types (in this case, int*).
If you can't get a perfect match, you look at all the base templates that you know about. In this case, there are two: f<T> and f<T*>. Note that unlike classes, function templates can't be partially specialised, so as far as the compiler is concerned these are completely separate overloads.
Clearly the f<T*> base template is a better match, so you instantiate it with T=int. Now, if you've already seen a specialisation for f<int*>, then you use that, and otherwise you generate the function.
Now here's the funny thing. If we change the order of your original code, to
template<class T> void f( T ); // (i)
template<> void f<>(int*); // (ii)
template<class T> void f( T* ); // (iii)
then the compiler now sees (ii) as a specialisation of (i) -- because it processes things in order, and at the point it reaches (ii) it doesn't know that (iii) exists yet! But since it only matches on base templates, it decides that (iii) is a better match than (i) -- and now (iii) does not have any specialisations, so you get the default instantiation.
It's all pretty confusing, and can trip up even the most experienced C++ programmers at times. So the basic rule is this: don't specialise function templates, but use normal function overloading instead. A regular old, non-template
void f(int*);
would be matched before anything else, and avoids this whole mess.
EDIT: n.m. requested references to the standard in the comments. I'm afraid I only have the C++03 version to hand, but here goes:
Paragraph 4.7.3.3: "A declaration of a function template or class template being explicitly specialized shall be in scope at the point of declaration of an explicit specialization.".
That's why in the above example, (ii) cannot be considered as an explicit specialisation of (iii), because (iii) is not yet in scope.
Section 4.8.3: "When a call to that [function] name is written... template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments."
In other words, (as I read it, anyway) it always looks at each base template no matter what -- providing an explicit specialisation makes no difference.
The same paragraph goes on: "For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to instantiate a single function template specialization which is added to the candidate functions set to be used in overload resolution."
So it's only at this point (as I read it) that explicit specialisations get taken into account.
Lastly, and perhaps most importantly in this case, section 13.3.3 deals with choosing the "best viable function" in the overload set. Two items are relevant:
F1 is better than F2 if: "F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.5.2". This is why the f<T*> version gets picked ahead of the f<T> version when trying to match f(int*) -- because it's a "more specialised" template
F1 is better than F2 if: "F1 is a non-template function and F2 is a function template specialization", which was the basis for my advice at the end of the original answer.
Phew!
Reading the C++11 standard I can't fully understand the meaning of the following statement. Example are very welcome.
Two sets of types are used to determine the partial ordering. For each
of the templates involved there is the original function type and the
transformed function type. [Note: The creation of the transformed type
is described in 14.5.6.2. — end note ] The deduction process uses the
transformed type as the argument template and the original type of the
other template as the parameter template. This process is done twice
for each type involved in the partial ordering comparison: once using
the transformed template-1 as the argument template and template-2 as
the parameter template and again using the transformed template-2 as
the argument template and template-1 as the parameter template
-- N3242 14.8.2.4.2
While Xeo gave a pretty good description in the comments, I will try to give a step-by-step explanation with a working example.
First of all, the first sentence from the paragraph you quoted says:
For each of the templates involved there is the original function type and the transformed function type. [...]
Hold on, what is this "transformed function type"? Paragraph 14.5.6.2/3 explains that:
To produce the transformed template, for each type, non-type, or template template parameter (including
template parameter packs (14.5.3) thereof) synthesize a unique type, value, or class template respectively
and substitute it for each occurrence of that parameter in the function type of the template [...]
This formal description may sound obscure, but it is actually very simple in practice. Let's take this function template as an example:
template<typename T, typename U>
void foo(T, U) // #1
Now since T and U are type parameters, the above paragraph is asking us to pick a corresponding type argument for T (whatever) and substitute it everywhere in the function signature where T appears, then to do the same for U.
Now "synthesizing a unique type" means that you have to pick a fictitious type you haven't used anywhere else, and we could call that P1 (and then pick a P2 for U), but that would make our discussion uselessly formal.
Let's just simplify things and pick int for T and bool for U - we're not using those types anywhere else, so for our purposes, they are just as good as P1 and P2.
So after the transformation, we have:
void foo(int, bool) // #1b
This is the transformed function type for our original foo() function template.
So let's continue interpreting the paragraph you quoted. The second sentence says:
The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template. [...]
Wait, what "other template"? We only have one overload of foo() so far. Right, but for the purpose of establishing an ordering between function templates, we need at least two of them, so we'd better create a second one. Let's use:
template<typename T>
void foo(T const*, X<T>) // #2
Where X is some class template of ours.
Now what with this second function template? Ah, yes, we need to do the same we previously did for the first overload of foo() and transform it: so again, let's pick some type argument for T and replace T everywhere. I'll pick char this time (we aren't using it anywhere else in this example, so that's as good as some fictitious P3):
void foo(char const*, X<char>) #2b
Great, now he have two function templates and the corresponding transformed function types. So how to determine whether #1 is more specialized than #2 or vice versa?
What we know from the above sentence is that the original templates and their transformed function types must be matched somehow. But how? That's what the third sentence explains:
This process is done twice for each type involved in the partial ordering comparison: once using the transformed template-1 as the argument template and template-2 as the parameter template and again using the transformed template-2 as the argument template and template-1 as the parameter template
So basically the transformed function type of the first template (#1b) is to be matched against the function type of the original second template (#2). And of course the other way round, the transformed function type of the second second template (#2b) is to be matched against the function type of the original first template (#1).
If matching will succeed in one direction but not in the other, then we will know that one of the templates is more specialized than the other. Otherwise, neither is more specialized.
Let's start. First of all, we will have to match:
void foo(int, bool) // #1b
Against:
template<typename T>
void foo(T const*, X<T>) // #2
Is there a way we can perform type deduction on T so that T const* becomes exactly int and X<T> becomes exactly bool? (actually, an exact match is not necessary, but there are really few exceptions to this rule and they are not relevant for the purpose of illustrating the partial ordering mechanism, so we'll ignore them).
Hardly. So let's try matching the other way round. We should match:
void foo(char const*, X<char>) // #2b
Against:
template<typename T, typename U>
void foo(T, U) // #1
Can we deduce T and U here to produce an exact match for char const* and X<char>, respectively? Sure! It's trivial. We just pick T = char const* and U = X<char>.
So we found out that the transformed function type of our first overload of foo() (#1b) cannot be matched against the original function template of our second overload of foo() (#2); on the other hand, the transformed function type of the second overload (#2b) can be matched against the original function template of the first overload (#1).
Conclusion? The second overload of foo() is more specialized than the first one.
To pick a counter-example, consider these two function templates:
template<typename T, typename U>
void bar(X<T>, U)
template<typename T, typename U>
void bar(U, T const*)
Which overload is more specialized than the other? I won't go through the whole procedure again, but you can do it, and that should convince you that a match cannot be produced in either direction, since the first overload is more specialized than the second one for what concerns the first parameter, but the second one is more specialized than the first one for what concerns the second parameter.
Conclusion? Neither function template is more specialized than the other.
Now in this explanation I have ignored a lot of details, exceptions to the rules, and cryptic passages in the Standard, but the mechanism outlined in the paragraph you quoted is indeed this one.
Also notice, that the same mechanism outlined above is used to establish a "more-specialized-than" ordering between partial specializations of a class template by first creating an associated, fictitious function template for each specialization, and then ordering those function templates through the algorithm described in this answer.
This is specified by paragraph 14.5.5.2/1 of the C++11 Standard:
For two class template partial specializations, the first is at least as specialized as the second if, given the
following rewrite to two function templates, the first function template is at least as specialized as the second
according to the ordering rules for function templates (14.5.6.2):
— the first function template has the same template parameters as the first partial specialization and has
a single function parameter whose type is a class template specialization with the template arguments
of the first partial specialization, and
— the second function template has the same template parameters as the second partial specialization
and has a single function parameter whose type is a class template specialization with the template
arguments of the second partial specialization.
Hope this helped.