Why is `"literal"` encouraged to decay to `const char*` in C++ argument type match?
c++, c++14
Solution
Why is `const char*` preferred over `const 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 over `const 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
Problem
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 to see if I can discriminate between: ``` const char * run_time; ``` and ``` "compile time" ``` I wrote the code below, and as shown, when I try `span >> "literal"` it invoked the `const char*` function. When I `#if 0`-out the `const char*` version, the template version gets called just fine. If I change the template version to take an rvalue-reference (&&) parameter for `literal`, it doesn't compile. If I add a `const char (&literal)[]` non-template version, the `const char*` version is still preferred. Removing the const-char* version, the template version is preferred. Can you explain this? In particular: - Why is `const char*` preferred over `const char (&)[N]`? - Why is `const char (&)[N]` preferred over `const char (&)[]` (non-template)? - Why is `const char (&&)[N]` unable to compile? - Is there a "right way" to capture literal strings? Thanks. ``` #include <iostream> using namespace std; #include <gsl/gsl> #include <type_name.h++> template<unsigned N> auto operator>>(gsl::span<const char*,-1>& spn, const char (&literal)[N]) -> gsl::span<const char*, -1>& { cout << "Got array: " << literal << endl; return spn; } auto operator>>(gsl::span<const char*,-1>& spn, const char *literal) -> gsl::span<const char*, -1>& { cout << "Got const-char*: " << literal << endl; return spn; } #if 0 #endif int main(int argc, const char *argv[]) { auto spn = gsl::span<const char*>(argv, argc); cout << type_name<decltype(spn)>() << endl; // gsl::span<const char *, -1> cout << type_name<decltype("literal")>() << endl; // char const (&)[8] cout << type_name<decltype(("literal"))>() << endl; // char const (&)[8] auto helpx = "literal"; cout << type_name<decltype(helpx)>() << endl; // const char * spn >> "literal"; // Got const-char*: literal return 0; } ``` Edit: In case it matters, I'm compiling with: ``` c++ --std=c++14 -Iinclude -c -o main.o main.c++ ``` And c++ says: ``` $ c++ --version Apple LLVM version 8.0.0 (clang-800.0.42.1) Target: x86_64-apple-darwin16.5.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin ```