Pointer Decay Takes Precedence Over Template Deduction
When writing code that operates on strings, it is common to encounter a dilemma where attempting to overload a function to accommodate both array-based and non-array-based string representations can result in unexpected behavior.
In this specific scenario, an initial function foo is defined to print the length of an array of characters:
template <size_t N> void foo(const char (&s)[N]) { std::cout << "array, size=" << N-1 << std::endl; }
This function accepts arrays of characters and outputs the array length. However, when extending foo to also handle non-array strings, ambiguity arises:
void foo(const char* s) { std::cout << "raw, size=" << strlen(s) << std::endl; }
The intention is for the second foo overload to handle non-array strings, but surprisingly, this extension leads to the original overload being bypassed for both array and non-array strings:
foo("hello") // prints raw, size=5
This unexpected behavior stems from the concept of pointer decay. Arrays are essentially pointers to their first element, and converting an array to a pointer is a very inexpensive operation. As a result, the compiler prioritizes the overload that accepts a pointer parameter, even though it requires an implicit conversion from the array argument.
To ensure the desired behavior, where the array-based overload is used for arrays, it is necessary to introduce an additional overload:
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; }
This overload uses template deduction and restricts its applicability to types that can be converted to character pointers. Through partial ordering, the compiler now correctly selects the appropriate foo function based on the argument type.
The above is the detailed content of Why Does Pointer Decay Override Template Deduction in C Function Overloading?. For more information, please follow other related articles on the PHP Chinese website!