I give details of my current understanding of the problem as an answer. I am not sure that this will be the last word on this issue, but it can serve as the basis for further discussion, if necessary. The comments from dyp, hvd, and Columbo were needed to find the various bits of information mentioned below.
As I suspected, the problem is related to the rules for partial ordering of function templates. Section [14.8.2.4] (Derivation of template arguments in partial ordering) says that after preliminary transformations that remove links and cv-qualifiers, type inference is performed as described in [14.8.2.5] (inference from template arguments from type) . This section is different from the one that refers to function calls β it will be [14.8.2.1] (subtracting the template arguments from the function call).
When template parameters are inferred from function argument types, several special cases are allowed; for example, the template parameter T used in a function parameter of type T* can be inferred when the argument of the function is T[i] , because conversion from an array to a pointer is allowed in this case. However, this is not the inference process that was used in partial ordering , although we are still talking about functions.
I think that a simple way to think about the rules for outputting a template argument during partial ordering is to say that they are the same rules as for outputting template arguments when comparing class specialties.
Clean like dirt? Perhaps a few examples will help.
This works because it uses rules to infer template arguments from a function call:
#include <iostream> #include <type_traits> template<typename T> void f(T*) { std::cout << std::is_same<T, int>::value << '\n'; } int main() { int a[3]; f(a); }
and prints 1 .
This is not so because it uses rules to infer template arguments from a type:
#include <iostream> template<typename T> struct A; template<typename T> struct A<T*> { static void f() { std::cout << "specialization\n"; } }; int main() { A<int[3]>::f(); }
and the error from Clang is
error: implicit instantiation of undefined template 'A<int [3]>'
Specialization cannot be used because T* and int[3] do not match in this case, so the compiler tries to instantiate the primary template.
This is the second type of output that was used in partial ordering.
Back to the function template declarations:
template<typename T> void f(T, const char*);
My description of the partial sequencing process will be:
- The output of the argument of the template C # 1 as a parameter and # 2 as an argument: we set the value of
M to replace N , T is displayed as int , but the parameter of type const char* does not match the argument of type char[M] , therefore # 2 is not , at least as specialized as # 1 for the second pair of types. - The output of the C # 2 template argument as a parameter and # 1 as an argument: we invent type
U to replace T , int and U do not match (different types), a parameter of type char[N] does not match an argument of type const char* , and the value of the parameter A non-type N pattern cannot be inferred from arguments, so # 1 is not at least as specialized as # 2 for any type pair.
Since in order to be selected, the template must be at least as specialized as the other for all types, it follows that none of the templates is more specialized than the other, and the call is ambiguous.
The explanation above is somewhat contrary to the description of a similar problem in Core Language Active Issue 1610 (link provided by hvd).
Example:
template<class C> void foo(const C* val) {} template<int N> void foo(const char (&t)[N]) {}
The author claims that, intuitively, the second template should be chosen as a more specialized one, and that this is currently not happening (no template is more specialized than the other).
He then explains that the reason is the removal of the const qualifier from const char[N] , which gives char[N] , which leads to output failure with the const C* parameter.
However, based on my current understanding, the deduction in this case will not end, const or no const . This is confirmed by current implementations in Clang and GCC: if we remove the const qualifier from the parameters of both function templates and call foo() with the argument char[3] , the call is still ambiguous. Arrays and pointers just don't match the current rules during partial ordering.
Having said that, I am not a member of the committee, so there may be more than I understand.
Update. I recently came across another active Core issue that dates back to 2003: issue 402 .
An example in it is equivalent to that located in 1610 . The remarks on the problem make it clear that the two overloads are disordered in accordance with the partial ordering algorithm in the form in which it exists, precisely because of the lack of decay rules for pointer matrices in partial ordering.
Last comment:
The view was expressed that it would be desirable to have this case, but we do not think that it is worth spending time on his work now. If at some point we consider some larger partial order changes, we will consider it again.
Thus, I am sure that the interpretation I gave above is correct.