While reading another question, i came to a problem with partial ordering, which i cut down to the following test-case
template
struct Cons
Here's my go at this. I agree with Charles Bailey that the incorrect step is to go from Const to ::Type*
void*
template
void f(T, typename Const::type*) { cout << "Const"; } // T1
template
void f(T, void*) { cout << "void*"; } // T2
The steps we want to take are:
14.5.5.2/2
Given two overloaded function templates, whether one is more specialized than another can be determined by transforming each template in turn and using argument deduction (14.8.2) to compare it to the other.
14.5.5.2/3-b1
For each type template parameter, synthesize a unique type and substitute that for each occurrence of that parameter in the function parameter list, or for a template conversion function, in the return type.
In my opinion, the types are synthesized as follows:
(Q, Const::Type*) // Q1
(Q, void*) // Q2
I don't see any wording that requires that the second synthesized parameter of T1 be void*. I don't know of any precedent for that in other contexts either. The type Const is perfectly valid type within the C++ type system.::Type*
So now we perform the deduction steps:
Q2 to T1
We try to deduce the template parameters for T1 so we have:
T is deduced to be QEven though parameter 2 is a non deduced context, deduction has still succeeded because we have a value for T.
Q1 to T2
Deducing the template parameters for T2 we have:
T is deduced to be Qvoid* does not match Const::Type*
so deduction failure.IMHO, here's where the standard lets us down. The parameter is not dependent so it's not really clear what should happen, however, my experience (based on a squinted read of 14.8.2.1/3) is that even where the parameter type P is not dependent, then the argument type A should match it.
The synthesized arguments of T1 can be used to specialize T2, but not vice versa. T2 is therefore more specialized than T1 and so is the best function.
UPDATE 1:
Just to cover the poing about Const being void. Consider the following example:::type
template
struct Const;
template
void f(T, typename Const::type*) // T1
{ typedef typename T::TYPE1 TYPE; }
template
void f(T, void*) // T2
{ typedef typename T::TYPE2 TYPE ; }
template<>
struct Const
{
typedef void type;
};
template<>
struct Const
{
typedef long type;
};
void bar ()
{
void * p = 0;
f (0, p);
}
In the above, Const is used when we're performing the usual overload resolution rules, but not when we get to the partial overloading rules. It would not be correct to choose an arbitrary specialization for Const. It may not be intuitive, but the compiler is quite happy to have a synthasized type of the form ::type
Const and to use it during type deduction.::type*
UPDATE 2
template
class Const
{
public:
typedef typename Const::type type;
};
template
class Const
{
public:
typedef void type;
};
template
void f(T (&)[I], typename Const::type*) // T1
{ typedef typename T::TYPE1 TYPE; }
template
void f(T (&)[I], void*) // T2
{ typedef typename T::TYPE2 TYPE ; }
void bar ()
{
int array[10];
void * p = 0;
f (array, p);
}
When the Const template is instantiated with some value I, it recursively instantiates itself until I reaches 0. This is when the partial specialization Const is selected. If we have a compiler which synthesizes some real type for the parameters of the function, then what value will the compiler choose for the array index? Say 10? Well, this would be fine for the above example but it wouldn't match the partial specialization Const which, conceptually at least, would result in an infinite number of recursive instantiations of the primary. Whatever value that it selected we could modify the end condition to be that value + 1, and then we'd have an infinite loop in the partial ordering algorithm.
I do not see how the partial ordering algorithm could correctly instantiate Const to find what type really is.