I\'m playing around with overloading operators in c++14, and I tried to match two types of arguments: any-old-const-char*, and a-string-literal.
That is, I\'m trying
13.3.3 Best viable function [over.match.best]
...
Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
...
(1.7) F1 is not a function template specialization and F2 is a function template specialization
actually non-template const char (&)[]
does not seem to compile at all because it is a reference to a non-bound array. It is possible to pass a pointer like this const char []
, but not array.
this should fail at least for the same reason as (2)
template< typename = void > void
foo(char const * & text)
{
::std::cout << "got ptr" << ::std::endl;
}
Note that providing another template taking a pointer won't work because both template specializations will be fine and we'll get ambiguous choice of overloaded functions.
Why is
const char*
preferred overconst char (&)[N]
?
The reason for this is rather technical. Even though the decay of a string literal from const char[N]
to const char*
is a conversion, it falls into the "lvalue transformation" category and is therefore considered by [over.ics.rank]/3 to be as good as no conversion at all. Since "no conversion" is required for either overload, the non-template overload wins.
Why is
const char (&)[N]
preferred overconst char (&)[]
(non-template)?
It is not possible to bind a reference to array of unknown bound to a value of type array of known bound. Instead, a reference to array of unknown bound can only be bound to values that are themselves arrays of unknown bound.
Why is
const char (&&)[N]
unable to compile?
A string literal is an lvalue so I'm not sure why you would expect this to work.
Is there a "right way" to capture literal strings?
You can use a helper function template that captures its argument using a forwarding reference so as to not destroy any type information (const char*
versus const char[N]
) then dispatch on the type using template specialization. You'll probably also want to use SFINAE to make sure it is disabled if anything other than a const char*
or const char[N]
is passed in. To wit,
template <bool b>
struct f_helper;
template <>
struct f_helper<true> {
void do_it(const char*) {
puts("pointer");
}
};
template <>
struct f_helper<false> {
template <std::size_t N>
void do_it(const char (&)[N]) {
printf("array of length %zd\n", N);
}
};
template <class T, class = typename std::enable_if<std::is_same<char*, std::decay_t<T>>::value ||
std::is_same<const char*, std::decay_t<T>>::value>::type>
void f(T&& s) {
f_helper<std::is_pointer<std::remove_reference_t<T>>::value>{}.do_it(s);
}
Coliru link: http://coliru.stacked-crooked.com/a/0e9681868d715e87