#include <iostream>
using namespace std;
void f(const char * const &s) {
    cout << "lvalue" << endl;
}
void f(const char * const &&s) {
    cout << "rvalue" << endl;
}
int main()
{
    char s[] = "abc";
    f("abc");
    f(s);
}
Output:
rvalue
rvalue
Why isn't the output "rvalue lvalue"?
Neither the string literal nor s are pointers (they are arrays), so the relevant section of the standard is [conv.array]:
An lvalue or rvalue of type "array of
NT" or "array of unknown bound ofT" can be converted to a prvalue of type "pointer toT". The result is a pointer to the first element of the array.
Note that
char const *p = s;
f(p);
prints "lvalue," to show that this works as you expect for pointers.
Addendum re: comment: In the case of
char *p = s;
f(p);
which prints "rvalue" if the rvalue overload exists but does not cause a compiler error if it is removed, two other sections of the standard come into play -- one of which seems to prohibit the binding of char* to char const *const & altogether, and the other opening a window back in.
The first is [dcl.init.ref]/4, where it is stated that
Given types "cv1
T1" and "cv2T2", "cv1T1" is reference-related to "cv2T2" ifT1is the same type asT2, orT1is a base-class ofT2. "cv1T1" is reference-compatible with "cv2T2" ifT1is reference-related toT2and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2. (...)
It goes on at length about precise rules for reference initialization, all of which is relevant but unfortunately far too long for a SO answer. The long story made short is that a reference to cv1 T1 can be initialized with an object of cv2 T2 if the two are reference-compatible.
What this Legalese means for our case is that char* and char const * are not reference-compatible (although char* and char *const would be), because char* is not char const * nor is one a base class of the other. This restriction makes sense if you consider the following illegal piece of code that would be legal otherwise:
const char c = 'c';
char *pc;
const char*& pcc = pc;   // #1: not allowed
pcc = &c;
*pc = 'C';               // #2: modifies a const object
This is adapted from a similar example in [conv.qual]/4 that uses a pointer to pointer to demonstrate the same problem.
[conv.qual] is also the other relevant section that opens the window back in. It says in [conv.qual]/1:
A prvalue of type "pointer to cv1
T" can be converted to a prvalue of type "pointer to cv2T" if "cv2T" is more cv-qualified than "cv1T"
It follows from all this that char* can be converted to char const *1 (which is reference-compatible with char const *const), which is why the code still compiles if the rvalue overload of f is removed. However, the result of this conversion is a prvalue, so if it is present, the rvalue overload is preferred in overload resolution.
1char* glvalue -> char* prvalue (by [conv.lval]) -> char const * prvalue)
s is an array lvalue (so is "abc", for that matter - string literals are lvalues). To get a pointer, the array-to-pointer conversion is performed. This conversion yields a pointer prvalue, which preferentially binds to the rvalue reference overload.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With