Understand the whole process of function template overload resolution

Viewed 138

I found out that the overload resolution process is the more complicated I have ever learned in C++, Therefore, bear in mind that this topic is relatively difficult for me to understand easily, so be patient with me.

I have two examples here, and I tried to parse each example separately and understand what actually the compiler is doing starting from the call to the overloaded template until determining which overload is the best viable.

First Example #1

template <class T> void f(const T&); // F1: reference to const T
template <class T> void f(const T*); // F2: pointer to const T
f((int*)0);

// matching F1:
// deduction: P = const T&, A = int* --> const T& = int* --> T = int*
// instantiates: void f(int *const&); A = int*, P = int *const&
// S1 conversion: int* --> int* const& (identity conversion?)

// matching F2:
// P = const T*, A = int* --> const T* = int*; T = int;
// instantaies: void f(const int*); A = int*, P = const int*;
// S2 conversion: int* --> const int* (qualification conversion)

Per [over.ics.rank]/3/3.2

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if

  • (3.2.1) S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 12.2.4.2.2, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence)
  • [..]

S1 is s a proper subsequence of S2: that's identity conversion is a proper subsequence of qualification conversion because the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence.

and per [over.match.best.general]/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.1) — for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2)
  • [..]

Here, the implicit conversions for all arguments of F1 (which is identity conversion) are "not worse" than the implicit conversions for all arguments of F2 (which is qualification conversion), and there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2. Then the viable function F1 is a better function than the viable function F2. Hence, a specialization of F1 gets chosen by the overload resolution for the given call.

My questions:

  • Have I parsed the whole process correctly?
  • Does the compiler need to perform partial ordering at this point? if yes, why?, even though we knowfrom ICS that F1 is the best viable candidate?

Second Example #2

template <class T> void f(const T&); // F1: reference to const T
template <class T> void f(T*);       // F2: pointer to T
f((int*)0);

// matching F1:
// deduction: P = const T&, A = int* --> const T& = int* --> T = int*
// instantiates: void f(int *const&); A = int*, P = int *const&
// S1 conversion: int* --> int* const& (identity conversion)

// matching F2:
// deduction: P = T*, A = int* --> T* = int*; T = int
// instantaies: void f(int*); A = int*, P = int*
// S2 conversion: int* --> int* (identity conversion)

Here neither rule in [over.ics.rank]/3/3.2 is applied; that's both S1 and S2 are identity conversions.

Then the compiler goes to the next step to check the rules defined in [over.match.best.general]/2. Indeed ICSi(F1) is not a worse conversion sequence than ICSi(F2), but the 2.1 bullet is not satisfied because ICSi(F1) is not a better conversion sequence than ICSi(F2). The implicit conversion sequence for the argument of F1 is actually the same (i.e not worse) as that of F2.

The compiler keeps checking the rules in [over.match.best.general]/2 until it hits bullet 2.5 which says:

  • [..]
  • (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 [..]

I am not going through the whole process of partial ordering, instead, I will summarize things as possible.

Adjustment Function Signature:

void f(T);  // Tem1
void f(T*); // Tem2

Transformed Function Signature:

void f(U1);  // Tra1
void f(U2*); // Tra2

Matching Tem1 against Tran2:

void f(T);  // Tem1
void f(U2*); // Tra2

// T = U2* - OK: T can be deduced from U2*

Matching Tem2 against Tran1:

void f(T*); // Tem2
void f(U1);  // Tra1

// T* = U1 - error: T cannot be deduced

So template referred by Tra2 is more specialized than the template referred by Tran1: F2 is more specialized than F1. Therefore a specialization of F2 will be selected by the overload resolution for the given call.

My questions:

  • Have I parsed the whole process correctly?
  • What I think (it might be incorrect) is that after template argument deduction, the compiler generates specializations for both overloads to match P/A pairs for the ICS process. Now does the compiler generates a specialization again for the more-specialized template?

Believe me, before posting this question, I searched a lot for a question that covers that whole process. What I need to know, Am I thinking right, and whether all quotes I provided are applied to the examples or not.

Sorry for taking long, and Thanks in advance.

0 Answers
Related