Why does pointer evasion take precedence over the displayed pattern?

Say I'm writing a function to print the length of a string:

template <size_t N> void foo(const char (&s)[N]) { std::cout << "array, size=" << N-1 << std::endl; } foo("hello") // prints array, size=5 

Now I want to extend foo to support non-arrays:

 void foo(const char* s) { std::cout << "raw, size=" << strlen(s) << std::endl; } 

But it turns out that this violates my original intended use:

 foo("hello") // now prints raw, size=5 

Why? Wouldn't that require conversion between arrays and pointers, while a pattern would be an exact match? Is there any way to guarantee that my array function will be called?

+50
c ++ arrays overload-resolution
Jan 30 '15 at 19:39
source share
1 answer

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); // Ambiguous } 



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 standard conversion sequence S1 is a better conversion sequence than the standard conversion sequence S2 if

    • S1 is the correct subsequence of S2 (comparing canonical transformation sequences defined in 13.3.3.1.1 excluding any Lvalue transformation ; an identity transformation sequence is considered to be a subsequence of any transformation without an identity sequence) or, if not this,

    • rank S1 better than rank S2 , or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below, or, if not the one

    • [..]

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

+42
Jan 30 '15 at 19:49
source share
— -



All Articles