Recently, I find that GCC
has changed the behavior during partial ordering, the concrete case is the following:
#include <iostream>
template<class T>
struct unknow_context{
using type = int;
};
template<class U>
void show(typename unknow_context<U>::type, U){ // candidate #1
std::cout<<"#1\n";
}
template<class T>
void show(int, T){ // candidate #2
std::cout<<"#2\n";
}
int main(){
show(0,0);
}
The result is, Clang prints #2
(any version of Clang
has printed a consistent result). However, GCC
has different behavior. The older version of GCC prints #2
, instead, the latest GCC complains the candidate functions are ambiguous. Let's see what the standard says about partial ordering.
temp.deduct.partial#2
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.
So, we can get two sets of P/A pair for the candidate #1
and #2
, respectively. One is the transformed #1
as A, the original #2
as P. The other is the original #1
as P, the transformed #2
as A. So the two sets will be given as the following:
#a
|--------|------------------------------------------|
| P (#2) | A (#1) |
|--------|------------------------------------------|
| int | typename unknow_context<UniqueA>::type |
|--------|------------------------------------------|
| T | UniqueB |
|--------|------------------------------------------|
T
can be deduced from UniqueB
. For the first set, the rule says:
If a particular P contains no template-parameters that participate in template argument deduction, that P is not used to determine the ordering.
So, we put them aside first and consider them later.
#b
|----------------------------------|-------|
|P (#1) |A (#2) |
|----------------------------------|-------|
| typename unknow_context<U>::type |int |
|----------------------------------|-------|
| U |UniqueA|
|----------------------------------|-------|
For a non-deduced context, its value can be obtained from elsewhere.
In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
So, we can ignore the pair typename unknow_context<U>::type
/ int
, and consider U
/ UniqueA
. U
can be deduced from UniqueA
.
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.
From #b
, we get the result that #2
is at least as specialized as #1
.
The question is in the #a set
. The second pair has no problem, we can say that the second part of #1
is at least as specialized as the second part of #2
.
However, the rule for whether a function template F is more specialized than function template G is defined as:
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.
So, we take attention to pair int / typename unknow_context<UniqueA>::type
, although the rule says "P is not used to determine the ordering". However, an important note in the standard says:
[ Note: Under [temp.deduct.call] and [temp.deduct.partial], if P contains no template-parameters that appear in deduced contexts, no deduction is done, so P and A need not have the same form. — end note ]
So, as far as now, from the P/A set #a
, #1
is still at least as specialized as #2
(deduction success). So, I think the latest GCC
should be correct (ambiguous, neither is more specialized than the other).
Question 1:
which compiler is correct?
Question 2:
whether the instantiation of specialization be performed during partial ordering? The standard seems to not specify whether the instantiation will be performed.
#include <iostream>
template<class T>
struct unknow_context{
using type = T;
};
template<class U>
void show(typename unknow_context<U>::type, U){
std::cout<<"#1\n";
}
template<class T>
void show(T, T){
std::cout<<"#2\n";
}
int main(){
show(0,0);
}
All chose #2
.
I'm concerned about the particular P/A pair
thereof, that is:
|----|------------------------------------------------------------|
|P |A |
|----|------------------------------------------------------------|
|T |typename unknow_context<UniqueA>::type /*Is it equivalent to|
| | UniqueA? */ |
|----|------------------------------------------------------------|
|T |UniqueA |
|----|------------------------------------------------------------|
whether typename unknow_context<UniqueA>::type
will be calculated to UniqueA
? It seems that All compiler treat typename unknow_context<UniqueA>::type
as a unique type rather than calculate it to UniqueA
.
(As this answer agrees with OP and disagrees with the implementations of both Clang and GCC (trunk), it may be a somewhat incomplete answer, but at the very least it highlights some existing issues with the partial ordering rules, particularly for partial ordering where non-deduced contexts are involved))
Let's first note that as of GCC 11(/trunk), both compilers agree with their interpretation, and pick candidate #2 as more specialized than candidate #1, and as per [over.match.best]/1.7 overload resolution chooses the former as the best viable function.
However, your argument of [temp.func.order] seems valid, particularly the emphasis on [temp.deduct.partial]/4:
meaning that
in [temp.deduct.partial]/10 should not consider the
(P, A)
pair(int, typename unknow_context<UniqueA>::type)
for ordering, and for the remaining pair candidate #1 is at least as specialized as candidate #2, meaning candidate #1 is at least a specified as candidate #2 as per [temp.deduct.partial]/10.Thus, I believe Clang is wrong, and that GCC is once again wrong as per GCC 11(/trunk), but as I highlight below, the partial ordering rules in edge-cases where non-deduced contexts are involved have been, historically, underspecified (02-0051/N1393 addressed many of them), and nowadays, at the very least vague (possibly still underspecified), as we see a lot implementation variance over them.
I am not sure of the most relevant section; it could arguably fall under [temp.inst]/9,
and the non-normative note of [temp.deduct]/8:
but yes, reasonably instantiation of the specialization of
unknown_context
would be required as part of template argument substitution of deduced arguments as part of partial ordering. We can use the injected friend trick to force a diagnosable ODR-violation if this holds true for the compiler's, and both GCC and Clang agrees, rejecting the following program:with the instructive error:
Historical unclarities in partial ordering and non-deduced contexts
We may start by the active/open CWG issue 455:
and note that compilers, notably Clang and GCC, consistently disagree on how to apply partial ordering rules in edge-cases where non-deduced contexts are involved.
Jason Merrill from GCC wrote CWG issue 1337, which was marked as a duplicate of CWG issue 455. Jason are active in a number of open GCC bug reports, particularly noting on
that [emphasis mine]
as well as to
that
Thus, as per the quotes above, their historically different interpretations of the (possible underspecified) standard seems to have been intentional, though; such implementation variance is typically a sign of vagueness in the related segment of the standard, at best (underspecified, at worst).
See also GCC bug report 67228.
Was the ambiguity error from GCC just a regression?
As noted above, GCC:s behaviour for the following program
is as follows:
#2
(same as clang)#2
I have not found an a bug reports for a [8/9/10] regression of this, but it seems GCC is now back to using the same interpretation as Clang (accepting the program and finding #2 as more specialized), which both of us seem to agree is wrong (candidate #1 should be considered at least as specialized as candidate #2).