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)
Standard conversion sequence
S1is a better conversion sequence than standard conversion sequenceS2if
- (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
F1is defined to be a better function than another viable functionF2if for all arguments i,ICSi(F1)is not a worse conversion sequence thanICSi(F2), and then
- (2.1) — for some argument
j,ICSj(F1)is a better conversion sequence thanICSj(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
F1is 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.