取消引用void指针时的reinterpret_cast行为

时间:2014-01-17 05:59:49

标签: c++ reinterpret-cast

在与某人讨论他在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)。

在这种情况下,T1voidT2intvoid*可使用{{1}转换为int* }}。因此满足所有要求。

根据该说明,reinterpret_castreinterpret_cast<int&>(*v)具有相同的效果,据我推算,它与*reinterpret_cast<int*>(&(*v))相同。

这是一个GCC和VC的错误,还是铿锵有误,我以某种方式误解了这个?

3 个答案:

答案 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的指针是违法的。