【发布时间】:2020-12-23 13:01:57
【问题描述】:
#include <iostream>
int main(){
int& rf = reinterpret_cast<int&>((int&&)1);
}
在此示例中,GCC 和 MSVC 不接受此代码。相反,Clang 编译成功。结果是here。
通过研究相关规则,对应的规则为:
expr.reinterpret.cast#11
如果可以使用 reinterpret_cast 将“指向 T1 的指针”类型的表达式显式转换为“指向 T2 的指针”类型,则可以将 T1 类型的泛左值(指定对象 x)强制转换为类型“对 T2 的引用” . 结果是
*reinterpret_cast<T2 *>(p)的结果,其中 p 是指向“指向 T1 的指针”类型的 x 的指针。 没有创建临时,没有复制,也没有构造函数 ([class.ctor])或调用转换函数 ([class.conv])。
看看这条规则,我觉得这条规则太模糊了。它说结果是*reinterpret_cast<T2 *>(p)。指针解引用规则定义为:
expr.unary#op-1
一元*运算符执行间接:应用它的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是一个左值引用该对象或表达式指向的函数。
所以,根据规则[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 都拒绝这个例子。什么鬼?与第一个示例的不同之处在于reinterpret_cast 的操作数这里是prvalue。只是,这种差异使Clang 不接受此代码?根据expr#10
当一个纯右值表达式作为一个操作数的操作数出现时,该操作数需要一个泛左值,临时实现转换将应用于将该表达式转换为一个 xvalue。
如上述规则中所述,临时实现转换应应用于prvalue 1,使其成为glvalue。经过这个转换,我看不出这个例子和第一个例子有什么不同。为什么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 将引用类型转换为另一个的结果总是一个左值 (*reinterpret_cast<T2 *>(p)),它暗示我们不能使用右值引用类型来绑定结果。不是吗?
这些例子是我在阅读 [expr.reinterpret.cast#11] 时想到的。不同的编译器给出非常不同的结果。有点奇怪。我还认为 [expr.reinterpret.cast#11] 给出了非常模糊的措辞。当我们给出右值引用或左值引用类型时,并没有说明操作数的值类别的要求。它只是说只要这些指针类型可以转换。该规则也没有说明结果是什么价值类别。我们只能通过表达式*reinterpret_cast<T2 *>(p)来推断它的值类别是左值。
如何解释这些问题。是不是[expr.reinterpret.cast#11]的缺陷?
【问题讨论】:
标签: c++ pointers language-lawyer