关于使用运算符 reinterpret_cast 将引用类型转换为另一个引用类型的奇怪问题
A weird issue about converting a reference type to another by using operator reinterpret_cast
#include <iostream>
int main(){
int& rf = reinterpret_cast<int&>((int&&)1);
}
在此示例中,GCC
和 MSVC
不接受此代码。相反,Clang
编译成功。结果是 here.
通过研究相关规则,对应的规则为:
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.
看看这个规则,我觉得这个规则好模糊。它说结果是 *reinterpret_cast<T2 *>(p)
的结果。取消引用指针的规则定义为:
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.
所以,根据规则[expr.unary#op-1],*reinterpret_cast<T2 *>(p)
的值类别是左值。而规则[expr.reinterpret.cast#11]只要求源指针类型可以使用reinterpret_cast
转换为目的指针类型,对其操作数没有其他要求,例如值类别。从这一点来说,我认为Clang给出了一个正确的过程。因为结果是左值,所以它可以绑定到左值引用。
现在,对第一个例子稍作修改
#include <iostream>
int main(){
int& rf = reinterpret_cast<int&>(1);
}
然后,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.
如上述规则所述,临时物化转换 应应用于纯右值 1
以使其成为左值。转换之后,我看不出这个例子和第一个例子有什么不同。为什么 Clang
现在拒绝它?
让我们继续考虑第三个例子
#include <iostream>
int main(){
int&& rf = reinterpret_cast<int&&>(1);
}
这次GCC
接受这个例子,而其他两个编译器报错,result就在这里
如果将声明更改为 int&& rf = reinterpret_cast<int&&>((int&&)1);
,则 Clang
将接受。
此外,[expr.reinterpret.cast#11] 说过使用 reinterpret_cast
将引用类型转换为另一种类型的结果总是 lvalue ( *reinterpret_cast<T2 *>(p)
),它暗示我们不能使用右值引用类型来绑定到结果。不是吗?
这些例子是我阅读[expr.reinterpret.cast#11]时想到的。不同的编译器给出非常不同的结果。有点奇怪。我还认为 [expr.reinterpret.cast#11] 的措辞非常模糊。当我们给出右值引用或左值引用类型时,它没有说操作数的值类别的要求。只是说只要这些指针类型都可以转换。该规则也没有说明结果是什么值类别。我们只能通过表达式*reinterpret_cast<T2 *>(p)
.
推断出它的值类别是左值
如何解读这些问题。是[expr.reinterpret.cast#11]的缺陷吗?
So, according to the rule [expr.unary#op-1], the value category of *reinterpret_cast<T2 *>(p)
is lvalue.
不,因为[expr.reinterpret.cast]/11 doesn't say the reference-casting expression is equivalent in every way to *reinterpret_cast<T2*>(p)
. It says the expression's result is the same as that expression's. Here "result" refers to [basic.lval]/5:
The result of a glvalue is the entity denoted by the expression.
reinterpret_cast
表达式的值类别仅由 [expr.reinterpret.cast]/1,
定义
The result of the expression reinterpret_cast<T>(v)
is the result of converting the expression v
to type T
. If T
is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T
is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and ....
所以 reinterpret_cast<int&>((int&&)1)
具有值类别左值,因为类型 int&
是左值引用类型,而不是因为一元 *
规则。其结果是 prvalue 1
.
的临时物化创建的对象
reinterpret_cast<int&>(1)
... temporary materialization conversion should apply to prvalue 1
to make it be a glvalue
否 - [basic.reinterpret.cast] 中唯一可能描述 reinterpret_cast<int&&>(1)
的部分将再次是 paragraph 11,用于将左值转换为引用类型。但是它的最后一句包括“没有创建临时文件”,所以临时实体化是不允许的。
因此 reinterpret_cast<int&>(1)
和 reinterpret_cast<int&&>(1)
都是错误格式的,gcc 在没有任何错误或警告的情况下接受 reinterpret_cast<int&&>(1)
是不正确的。
#include <iostream>
int main(){
int& rf = reinterpret_cast<int&>((int&&)1);
}
在此示例中,GCC
和 MSVC
不接受此代码。相反,Clang
编译成功。结果是 here.
通过研究相关规则,对应的规则为:
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.
看看这个规则,我觉得这个规则好模糊。它说结果是 *reinterpret_cast<T2 *>(p)
的结果。取消引用指针的规则定义为:
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.
所以,根据规则[expr.unary#op-1],*reinterpret_cast<T2 *>(p)
的值类别是左值。而规则[expr.reinterpret.cast#11]只要求源指针类型可以使用reinterpret_cast
转换为目的指针类型,对其操作数没有其他要求,例如值类别。从这一点来说,我认为Clang给出了一个正确的过程。因为结果是左值,所以它可以绑定到左值引用。
现在,对第一个例子稍作修改
#include <iostream>
int main(){
int& rf = reinterpret_cast<int&>(1);
}
然后,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.
如上述规则所述,临时物化转换 应应用于纯右值 1
以使其成为左值。转换之后,我看不出这个例子和第一个例子有什么不同。为什么 Clang
现在拒绝它?
让我们继续考虑第三个例子
#include <iostream>
int main(){
int&& rf = reinterpret_cast<int&&>(1);
}
这次GCC
接受这个例子,而其他两个编译器报错,result就在这里
如果将声明更改为 int&& rf = reinterpret_cast<int&&>((int&&)1);
,则 Clang
将接受。
此外,[expr.reinterpret.cast#11] 说过使用 reinterpret_cast
将引用类型转换为另一种类型的结果总是 lvalue ( *reinterpret_cast<T2 *>(p)
),它暗示我们不能使用右值引用类型来绑定到结果。不是吗?
这些例子是我阅读[expr.reinterpret.cast#11]时想到的。不同的编译器给出非常不同的结果。有点奇怪。我还认为 [expr.reinterpret.cast#11] 的措辞非常模糊。当我们给出右值引用或左值引用类型时,它没有说操作数的值类别的要求。只是说只要这些指针类型都可以转换。该规则也没有说明结果是什么值类别。我们只能通过表达式*reinterpret_cast<T2 *>(p)
.
如何解读这些问题。是[expr.reinterpret.cast#11]的缺陷吗?
So, according to the rule [expr.unary#op-1], the value category of
*reinterpret_cast<T2 *>(p)
is lvalue.
不,因为[expr.reinterpret.cast]/11 doesn't say the reference-casting expression is equivalent in every way to *reinterpret_cast<T2*>(p)
. It says the expression's result is the same as that expression's. Here "result" refers to [basic.lval]/5:
The result of a glvalue is the entity denoted by the expression.
reinterpret_cast
表达式的值类别仅由 [expr.reinterpret.cast]/1,
The result of the expression
reinterpret_cast<T>(v)
is the result of converting the expressionv
to typeT
. IfT
is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; ifT
is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and ....
所以 reinterpret_cast<int&>((int&&)1)
具有值类别左值,因为类型 int&
是左值引用类型,而不是因为一元 *
规则。其结果是 prvalue 1
.
reinterpret_cast<int&>(1)
... temporary materialization conversion should apply to prvalue1
to make it be a glvalue
否 - [basic.reinterpret.cast] 中唯一可能描述 reinterpret_cast<int&&>(1)
的部分将再次是 paragraph 11,用于将左值转换为引用类型。但是它的最后一句包括“没有创建临时文件”,所以临时实体化是不允许的。
因此 reinterpret_cast<int&>(1)
和 reinterpret_cast<int&&>(1)
都是错误格式的,gcc 在没有任何错误或警告的情况下接受 reinterpret_cast<int&&>(1)
是不正确的。