【问题标题】:reinterpret_cast behavior when dereferencing a void pointer取消引用 void 指针时的 reinterpret_cast 行为
【发布时间】:2014-02-06 08:53:33
【问题描述】:

在和某人争论他在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 都因我所期待的错误而失败,抱怨代码试图取消引用 reinterpret_cast 中的 void*。所以我决定在标准中查找这个。从 N3797,§5.2.10/11 [expr.reinterpret.cast]

如果类型“指向T1”的表达式可以显式转换为类型“指向T2”的类型,则类型为T1 的泛左值表达式可以转换为类型“引用T2”使用reinterpret_cast。结果引用与源 glvalue 相同的对象,但具有指定的类型。 [ 注意: 也就是说,对于左值,引用转换 reinterpret_cast&lt;T&amp;&gt;(x) 与使用内置 &amp;* 运算符的转换 *reinterpret_cast&lt;T*&gt;(&amp;x) 具有相同的效果(对于 @ 987654338@)。 —结束说明 ] 不创建临时文件,不制作副本,也不调用构造函数 (12.1) 或转换函数 (12.3)。

在这种情况下,T1voidT2intvoid* 可以使用 reinterpret_cast 转换为 int*。所以所有的要求都满足了。

根据注释,reinterpret_cast&lt;int&amp;&gt;(*v)*reinterpret_cast&lt;int*&gt;(&amp;(*v)) 具有相同的效果,据我估计与*reinterpret_cast&lt;int*&gt;(v) 相同。

那么这是 GCC 和 VC 的错误,还是 clang 并且我以某种方式误解了这一点?

【问题讨论】:

  • 如果您使用void* v = &amp;i; *v; 会怎样?铿锵仍然没有抱怨。 Should I use static_cast or reinterpret_cast when casting a void* to whatever 看起来很有趣。 (警告:旧的,信息可能已经过时了。)
  • 根据this,看起来&amp;*ptr 是合法的,但不等同于ptr(我猜这就是你在最后一个reinterpret_cast 中的意思)。因此,指针仍然必须被取消引用。
  • @chris 在您链接到的问题的 cmets 中,C99 明确声明 &amp;*E 的 Oli Charlesworth saysE 相同。所以这部分确实成功了。我想剩下的问题是如果Evoid,一个不完整的类型怎么办。
  • @Praetorian,是的,并且该注释仅在 C 中,而不是 C++。整个话题很混乱,但即使void *p; &amp;*p; 是真正的noop,在C 中也应该可以正常工作。

标签: c++ reinterpret-cast


【解决方案1】:

如果vvoid * 类型,那么*v 就没有意义了。问题不在于强制转换,问题在于取消引用指向 void 的指针是非法的。

【讨论】:

    【解决方案2】:

    问题是,演员表中的表达式是无效的。根据 §5.3.1 一元运算符

    一元 * 运算符执行间接:应用它的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是指向对象的左值或表达式指向的函数。如果表达式的类型是“指向 T 的指针”,则结果的类型是“T”。 [注意:指向不完整类型(除了 cv void)的指针可以被取消引用。这样获得的左值可以以有限的方式使用(例如,初始化引用);此左值不得转换为纯右值,见 4.1。 ——尾注]

    void 不是对象类型。所以事情就到此为止了,整个表达是无效的。 clang 似乎在这里弄错了。

    此外,void 类型只能在 §3.9.1 基本类型的特定情况下使用:

    void 类型的表达式只能用作表达式语句 (6.2)、逗号表达式 (5.18) 的操作数、?: (5.16) 的第二个或第三个操作数、typeid 或decltype,作为返回类型为 void 的函数的返回语句 (6.6.3) 中的表达式,或作为显式转换为 cv void 类型的操作数。

    因此,即使解除引用是合法的,您也不能使用 void“对象”作为转换中的源(转换为 void 除外)。

    reinterpret_cast&lt;int&amp;&gt;(*v) 可能确实与*reinterpret_cast&lt;int*&gt;(&amp;(*v)) 具有相同的效果当这两个表达式都有效时(并且没有花哨的运算符重载会破坏这一天)。仅仅因为一个表达式可以以有效的形式重写并不意味着它本身就是有效的。

    【讨论】:

    • 我同意,如果您孤立地查看operator*,则该表达式没有意义。我的猜想是&amp;(*p) 等价于p,从cmets 中链接到的问题@chris 看来情况并非如此。
    • 我收回这一点,根据this 的评论,C99 确实声明&amp;*pp 相同。
    【解决方案3】:

    void 类型的表达式在return 语句中被允许作为一个主要的语法设备,您也可以将表达式转换为void,但仅此而已:没有void 类型的泛泛值,void 类型的表达式不引用内存。因此,标准中引用的以左值开头的段落不适用。因此,clang 是错误的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-23
      • 2011-06-17
      • 1970-01-01
      • 2012-03-25
      • 2017-11-03
      相关资源
      最近更新 更多