#include <iostream>
int main(){
int& rf = reinterpret_cast<int&>((int&&)1);
}
In this example, GCC and MSVC do not accept this code. Instead, Clang compiles it successfully. The result is here.
Through researching the relevant rule, the corresponding rule is:
expr.reinterpret.cast#11
A glvalue of type T1, designating an object x, can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result is that of
*reinterpret_cast<T2 *>(p)where p is a pointer to x of type “pointer to T1”. No temporary is created, no copy is made, and no constructors ([class.ctor]) or conversion functions ([class.conv]) are called.
Look at this rule, I think this rule is so vague. It says the result is that of *reinterpret_cast<T2 *>(p). The rule for de-referencing pointer is defined as:
expr.unary#op-1
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
So, according to the rule [expr.unary#op-1], the value category of *reinterpret_cast<T2 *>(p) is lvalue. And the rule [expr.reinterpret.cast#11] only requires that the source pointer type can be converted to the destination pointer type by using reinterpret_cast, there are no other requirement such as value category for its operand. From this point, I think Clang gives a right process. Because the result is lvalue, hence it can be bound to lvalue reference.
Now, give a little modification to the first example
#include <iostream>
int main(){
int& rf = reinterpret_cast<int&>(1);
}
Then, three compilers all reject this example. What's the hell? The difference with the first example is the operand of reinterpret_cast here is prvalue. Merely, this difference make Clang don't accept this code? According to expr#10
Whenever a prvalue expression appears as an operand of an operator that expects a glvalue for that operand, the temporary materialization conversion is applied to convert the expression to an xvalue.
As said in the above rule, temporary materialization conversion should apply to prvalue 1 to make it be a glvalue. After this conversion, I don't see that this example has any difference with the first example. Why Clang now reject it?
Let us continue consider the third example
#include <iostream>
int main(){
int&& rf = reinterpret_cast<int&&>(1);
}
This time GCC accept this example while the other two compiler report an error, the result is here.
If change the declaration to int&& rf = reinterpret_cast<int&&>((int&&)1); then Clang will accept that.
In addition, the [expr.reinterpret.cast#11] has said that the result of converting reference type to another by using reinterpret_cast always a lvalue (*reinterpret_cast<T2 *>(p)), it hints we cannot use a rvalue reference type to bind to the result. Isn't it?
These examples are thought out when I read the [expr.reinterpret.cast#11]. Different compilers give very different result. It's weird. I also think that [expr.reinterpret.cast#11] gives a very vague wording. It didn't say the requirement of value category of the operand when we give a rvalue reference or lvalue reference type. It just says that as long as these pointer types can be converted. The rule also didn't say what value category the result is. We can only infer that its value category is lvalue through the expression *reinterpret_cast<T2 *>(p).
How to interpret these issues. Is it a defect of [expr.reinterpret.cast#11]?