【问题标题】:C++ how does cast with reference work?C ++如何使用参考进行转换?
【发布时间】:2015-08-28 20:56:13
【问题描述】:

谁能解释以下代码中发生了什么?

char cd[1024];
unsigned short int & messageSize =reinterpret_cast<unsigned short int&>(*cd);

它是否通过引用获取 cd 的前 2 个字符并将其转换为 16 位 int? 当我删除“&”时,编译器抱怨无法从 char 转换为 unsigned short int。

unsigned short int messageSize =reinterpret_cast<unsigned short int>(*cd);

【问题讨论】:

  • (int&amp;)x*(int *)&amp;x 相同

标签: c++ casting char


【解决方案1】:

reinterpret_cast 的“直观”含义是“获取一个位序列并将其视为该位序列具有不同类型”。这对于charunsigned short 类型是不可能的,因为它们的宽度不同。

对于第一种情况,直觉是:reinterpret_cast 将左值引用视为指向它所引用类型的指针(并将提及的转换应用于该指针)。

标准上说:

4.2 数组到指针的转换[conv.array]

  1. “N T 数组”或“T 未知边界数组”类型的左值或右值可以转换为“指向 T 的指针”类型的纯右值。结果是指向数组第一个元素的指针。

和:

5.3.1 一元运算符 [expr.unary.op]

  1. 一元* 运算符执行间接:应用它的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是引用对象或函数的左值 表达式所指向的。如果表达式的类型是“指向 T 的指针”,则结果的类型是 “T”。

因此,在取消引用 *cd 之后,我们将获得一个类型为 char 的左值(就像您编写 cd[0] 一样)。

5.2.10 重新解释演员表 [expr.reinterpret.cast]

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

也就是说,你有类似的东西

*reinterpret_cast<unsigned short *>(&cd[0])

但也许比以上所有内容更重要的是:

3.10 左值和右值 [basic.lval]

如果程序尝试通过以下方式访问对象的存储值 行为是以下类型之一以外的泛左值 未定义:

  • 对象的动态类型,
  • 对象动态类型的 cv 限定版本,
  • ...
  • char 或 unsigned char 类型。

也就是说,将“对 char 的引用”绑定到“unsigned short”类型的对象是可以的。但是反之亦然(即,如您的示例中那样)是不太好的,因为访问此类引用会调用未定义的行为。

【讨论】:

  • unsigned short 视为char(&amp;)[sizeof(unsigned short)] 是可以的。在 OP 的原始示例中要注意的是 char 数组不能保证正确对齐。但是 +1 以获得最完整的答案。
【解决方案2】:

有引用的强制转换与没有引用的相同强制转换有一点不同——没有引用的强制转换会创建一个新的临时对象,而有引用的强制转换会更改已经存在的对象的类型。这在许多情况下很重要,例如,在您的情况下,因为您将结果分配给非常量引用。非常量引用不能用临时对象初始化。

顺便说一句,您知道您在这里所做的是违反类型别名规则,并且会产生未定义的行为吗?

【讨论】:

  • 在示例中 *cd 只是一个字符,如何将其转换为 16 位短字符?你能告诉我我到底违反了什么吗?
  • 您不能通过在 char 类型之间使用别名来违反别名。但是,OP 的示例可能具有未定义的行为,因为不能保证 char 数组对于强制转换类型具有正确的对齐方式。
  • @GManNickG OP 的示例违反了严格的别名。你误解了规则。允许使用 char 类型来读取/写入其他对象,但反之亦然。
  • @MonsterHunter *cd 不访问内存位置,它指定它。所以它只是一个字符并不重要;我们可以将reinterpret_cast 指向一个更大类型的引用,这将指定一个更大类型的位置,该位置从*cd 开始的相同字节开始。
【解决方案3】:

unsigned short int &amp; messageSize 表示messageSize 是一个unsigned short int 类型的变量,并且将存储该变量的内存区域作为初始值设定项。

初始化器=reinterpret_cast&lt;unsigned short int&amp;&gt;(*cd) 说:获取cd 指向的位置的内存,并假装它包含unsigned short int

结果是,如果您尝试读写messageSize,那么您将尝试在包含其他内容的内存位置读写unsigned short int。这会导致未定义的行为。

在某些情况下,可以假装一个内存位置包含一个实际上不包含的对象;这不是其中的一个。

如果您的编译器没有执行别名优化,那么它可能会出现,就好像您的代码现在“工作”一样。但是代码被破坏了。

【讨论】:

    【解决方案4】:
    reinterpret_cast<unsigned short int&>(*cd);
    

    类似于

    *reinterpret_cast<unsigned short int*>(cd);
    

    【讨论】:

    • @SergeyA See C++14 [expr.reinterpret.cast]/11, " 对于左值,引用转换 reinterpret_cast&lt;T&amp;&gt;(x) 与转换 *reinterpret_cast&lt;T*&gt;(&amp;x) 具有相同的效果&amp;* 运营商"
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-20
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多