【发布时间】:2019-05-28 11:17:39
【问题描述】:
似乎普遍认为通过reinterpret_cast 进行类型双关语在某种程度上是被禁止的(正确地:“未定义的行为”,即“behavior for which this International Standard imposes no requirements”,并明确指出实现可能 定义行为)在 C++ 中。我是否不正确地使用以下理由不同意,如果是,为什么?
[expr.reinterpret.cast]/11 状态:
如果类型“指向
T1”的表达式可以显式转换为类型“指向T2”的类型,则类型为T1的泛左值表达式可以转换为类型“引用T2”使用reinterpret_cast。结果引用与源 glvalue 相同的对象,但具有指定的类型。 [ 注意:也就是说,对于左值,引用转换reinterpret_cast<T&>(x)与使用内置&和*运算符的转换*reinterpret_cast<T*>(&x)具有相同的效果(对于reinterpret_cast<T&&>(x)也是如此)。 — 尾注 ] 不创建临时,不复制,不调用构造函数或转换函数。
带脚注:
75) 这有时被称为双关语。
/11 通过示例隐含地带有 /6 到 /10 的限制,但也许最常见的用法(双关语 objects)在 [expr.reinterpret.cast]/7 中得到解决:
对象指针可以显式转换为不同类型的对象指针。当对象指针类型的prvalue
v转换为对象指针类型“指向cv T的指针”时,结果为static_cast<cv T*>(static_cast<cv void*>(v))。 [ 注意:将“指向T1的指针”类型的纯右值转换为“指向T2的指针”类型(其中T1 and T2是对象类型,T2的对齐要求并不比@987654351更严格@) 并返回其原始类型会产生原始指针值。 — 尾注 ]
显然,目的不能转换为/从指针或引用void,如:
- /7 中的示例清楚地表明,
static_cast在指针的情况下就足够了,[expr.static.cast]/13 和 [conv.ptr]/2 也是如此;和 - [conversions to] 对
void的引用是prima facie invalid。
此外,[basic.lval]/8 声明:
如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义:
(8.1) 对象的动态类型,
(8.2) 对象动态类型的 cv 限定版本,
(8.3) 一种类似于对象的动态类型,
(8.4) 对象的动态类型对应的有符号或无符号类型,
(8.5) 有符号或无符号类型,对应于对象动态类型的 cv 限定版本,
(8.6) 一种聚合或联合类型,在其元素或非静态数据成员(递归地包括子聚合或包含联合的元素或非静态数据成员)中包含上述类型之一,
(8.7) 一种类型,它是对象的动态类型的(可能是 cv 限定的)基类类型,
(8.8) char、unsigned char 或 std::byte 类型。
如果我们返回[expr.reinterpret.cast]/11 片刻,我们会看到“结果引用同一个对象作为源glvalue,但具有指定的类型。 "这在我看来是一个明确的声明,即reinterpret_cast<T&>(v) 的结果是对T 类型的对象的左值引用,显然是“通过”动态类型的“左值”访问该对象对象”。这句话还解决了[basic.life] 的各个段落通过虚假声明适用的论点,即此类转换的结果引用了T 类型的新对象,其生命周期尚未开始,刚刚发生 驻留在与v 相同的内存地址。
明确定义此类转换只是为了禁止结果的标准定义使用似乎是荒谬的,特别是鉴于脚注75指出这种[参考]转换是“有时称为双关语。”
请注意,我参考的是 C++17 (N4659) 的最终公开可用草案,但所讨论的语言从 N3337 (C++11) 到 N4788 (C++20 WD) 几乎没有变化(提示链接可能会参考以后的草案及时)。事实上,the footnote 到 [expr.reinterpret.cast]/11 在最近的草稿中更加明确:
当结果引用与源glvalue相同的对象时,这有时被称为类型双关。
【问题讨论】:
-
我没有注意到这种信念甚至被狭隘地持有。
-
这对我来说是一个明确的声明,即 reinterpret_cast
(v) 的结果是对 T 类型对象的左值引用,显然是“通过左值访问” of" "对象的动态类型" 。不,你错了。我不明白你为什么这么想。如果你访问结果,你通过T类型访问它,而不是v的动态类型。 -
glvalue 没有动态类型。 glvalue 是一个值,而值只有一个类型。唯一具有动态类型的实体是对象。值不是对象。
-
不存在
T类型的对象(reinterpret_cast 不创建对象) -
@MichaelKenzel 不; 动态类型 是表达式具有的属性(它不是对象的属性)。一个对象只有它的类型。在表达式指定基类子对象的情况下,表达式可能具有与其静态类型不同的动态类型
标签: c++ casting language-lawyer reinterpret-cast type-punning