The main reason for this (standard-consistent) ambiguity, apparently, lies in the cost of the conversion: overload resolution tries to minimize the operations performed to convert the argument to the corresponding parameter. An array is actually a pointer to its first element, although it is decorated with some information such as compilation time. Converting from an array to a pointer does not cost more than, for example, saving the address of the array itself or initializing a link to it. From this point of view, the ambiguity seems justified, although conceptually it is unintuitive (and may be subparagraph). In fact, this argument applies to all Lvalue transformations, as suggested in the quote below. Another example:
void g() {} void f(void(*)()) {} void f(void(&)()) {} int main() { f(g);
The following is required standard. Functions that are not specializations of any function template are preferable to those that are if they both correspond equally well (see [Over.match.best] / (1.3), (1.6)). In our case, the conversion is performed by the conversion between arrays and pointers, which is an Lvalue conversion with the Exact match rank (according to table 12 in [over.ics.user]). [Over.ics.rank] / 3:
The first marker point excludes our transform (since this is an Lvalue transform). The second requires a difference in ranks that are not there, since both conversions have an Exact match rating; "Rules in the following paragraph", i.e. In [over.ics.rank] / 4, also do not cover conversions between arrays and pointers.
So believe it or not, neither of the two conversion sequences is better than the other, and thus char const* -overload is chosen.
Possible workaround: also define the second overload as a function template, then partial ordering inserts and selects the first.
template <typename T> auto foo(T s) -> std::enable_if_t<std::is_convertible<T, char const*>{}> { std::cout << "raw, size=" << std::strlen(s) << std::endl; }
Demo
Columbo Jan 30 '15 at 19:49 2015-01-30 19:49
source share