#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
N
T
" 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
" ifT1
is the same type asT2
, orT1
is a base-class ofT2
. "cv1T1
" is reference-compatible with "cv2T2
" ifT1
is reference-related toT2
and 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