How to know if you can bind a const
reference T1
to T2
?
I used to think that you can bind const
reference T1
to type T2
only if T2
is convertible to T1
.
But since the following compiles:
char x[10];
const char (&y)[10] = x;
that should not be the case, since char[10]
is not convertible to const char[10]
(correct me if I'm wrong). So, what are the rules for being able to bind const
references to different types ? Is there just an additional rule like: for any type T
you can bind a const
reference T
to it ?
CodePudding user response:
The standard describes the reference binding rules in [dcl.init.ref]/4 and [dcl.init.ref]/5. There is a rather long list of rules, but the bits most relevant to your question are:
Given types “cv1 T1” and “cv2 T2”, “cv1 T1” is reference-related to “cv2 T2” if T1 is similar ([conv.qual]) to T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence ([conv]).
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
— If the reference is an lvalue reference and the initializer expression
— is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, or
[...]
then the reference binds to the initializer expression lvalue [...]
In your case, T1
would be const char [10]
and T2
would be char [10]
. T1
is reference-compatible with T2
because T2*
can be converted to T1*
, as it only requires adding const
-qualification to the pointed type, which is a standard conversion.
As you can see in the referenced sections, this is not the only case where reference binding is allowed - another case, for example, is binding a reference to a result of conversion (including user-defined). const
references are also special as they are allowed to bind to rvalues.
Note that this is indeed different from your previous understanding - T2
may not be convertible to T1
while you may be able to bind a reference still. Here's an example:
struct A
{
A(int);
A(A const&) = delete;
};
struct B : A
{
B() : A(10) {}
};
B b;
A& ra = b; // ok, binds to base subobject A of b
A a = b; // fail, A cannot be constructed from an object of B