在与某人讨论他在this answer的评论主题中提出的建议时,我遇到了一些gcc4.8和VS2013拒绝编译的代码,但是clang愉快地接受它并显示正确的结果。
#include <iostream>
int main()
{
int i{ 5 };
void* v = &i;
std::cout << reinterpret_cast<int&>(*v) << std::endl;
}
Live demo。 GCC和VC都因我预期的错误而失败,抱怨代码试图在void*
内取消引用reinterpret_cast
。所以我决定在标准中查看这个。来自N3797,§5.2.10/ 11 [expr.reinterpret.cast]
如果类型为“
T1
的指针”的表达式可以显式转换为“T2
”,则可以将类型T1
的glvalue表达式强制转换为类型“T2
”使用reinterpret_cast
指向reinterpret_cast<T&>(x)
的指针。结果引用与源glvalue相同的对象,但具有指定的类型。 [注意:也就是说,对于左值,参考广告*reinterpret_cast<T*>(&x)
与内置&
和{{1}的转化*
具有相同的效果运算符(类似于reinterpret_cast<T&&>(x)
)。 -end note ]不创建临时,不进行复制,也不调用构造函数(12.1)或转换函数(12.3)。
在这种情况下,T1
为void
,T2
为int
,void*
可使用{{1}转换为int*
}}。因此满足所有要求。
根据该说明,reinterpret_cast
与reinterpret_cast<int&>(*v)
具有相同的效果,据我推算,它与*reinterpret_cast<int*>(&(*v))
相同。
这是一个GCC和VC的错误,还是铿锵有误,我以某种方式误解了这个?
答案 0 :(得分:3)
事实是,演员表内的表达无效。根据§5.3.1一元运算符:
一元*运算符执行间接:它所应用的表达式应为指向对象类型的指针,或指向函数类型的指针,结果是引用该对象的左值或表达式指向的函数。如果表达式的类型是“指向T的指针”,则结果的类型为“T”。[注意:可以取消引用指向不完整类型(cv void除外)的指针。由此获得的左值可以以有限的方式使用(例如,初始化参考);这个左值不能转换为prvalue,见4.1。 - 结束说明]
void
不是对象类型。所以事情停在那里,整个表达无效。 clang似乎在这里弄错了。
此外,根据§3.9.1基本类型,void
类型只能在特定情况下使用:
void类型的表达式只能用作表达式语句(6.2),作为逗号表达式(5.18)的操作数,作为?:( 5.16)的第二个或第三个操作数,作为typeid的操作数或decltype,作为返回类型为void的函数的返回语句(6.6.3)中的表达式,或者作为显式转换为cv void类型的操作数。
所以,即使derefence是合法的,你也不能使用void
“对象”作为演员表中的来源(除了演员表无效)。
reinterpret_cast<int&>(*v)
可能确实与*reinterpret_cast<int*>(&(*v))
具有相同的效果(并且当天没有花哨的运算符重载)。仅仅因为表达式可以用有效的形式重写,并不意味着它本身是有效的。
答案 1 :(得分:3)
类型void
的表达式被允许作为return
语句中的主要语法设备,你也可以将表达式转换为void
,但这就是全部:没有类型为void
的glvalues,类型为void
的表达式不指内存。因此,从glvalue开始的标准引用的段落不适用。因此,铿锵有误。
答案 2 :(得分:1)
如果v
的类型为void *
,那么*v
就没有意义了。问题不在于演员,问题在于取消引用void的指针是违法的。