I've written the following example:
#include <iostream>
volatile int&& bar()
{
return 1;
}
int main()
{
const int& i = bar(); //error: binding of reference to type 'const int'
//to a value of type 'volatile int' drops qualifiers
}
But if we replace int&&
with int
it works fine:
#include <iostream>
volatile int bar()
{
return 1;
}
int main()
{
const int& i = bar(); //OK
}
that's not exactly clear. What the Standard says is (8.5.3/5 [dcl.init.ref]
):
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
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an lvalue of type “cv3 T3,” where “cv1 T1” is reference-compatible with “cv3 T3” 108 (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6) and choosing the best one through overload resolution (13.3)), then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).
[...]
— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
Well, in the first example we have an rvalue of type volatile int&&
. And the 'otherwise'
case is applicable to both two examples. But 5/5 [expr]
says:
If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis
So, essentially we have an rvalue of type volatile int
instead of volatile int&&
, which means both these two examples shall work in the same way.