【发布时间】:2018-05-17 04:06:17
【问题描述】:
示例代码:
struct S { int x; };
int func()
{
S s{2};
return (int &)s; // Equivalent to *reinterpret_cast<int *>(&s)
}
我认为这是常见的,并且被认为是可以接受的。该标准确实保证结构中没有初始填充。但是这种情况并没有在严格的别名规则(C++17 [basic.lval]/11)中列出:
如果程序尝试通过非下列类型之一的左值访问对象的存储值,则行为未定义:
- (11.1) 对象的动态类型,
- (11.2) 对象动态类型的 cv 限定版本,
- (11.3) 与对象的动态类型类似(如 7.5 中定义)的类型,
- (11.4) 对象的动态类型对应的有符号或无符号类型,
- (11.5) 有符号或无符号类型,对应于对象动态类型的 cv 限定版本,
- (11.6) 一种聚合或联合类型,在其元素或非静态数据成员(递归地包括子聚合或包含联合的元素或非静态数据成员)中包含上述类型之一,李>
- (11.7) 是对象动态类型的(可能是 cv 限定的)基类类型,
- (11.8) char、unsigned char 或 std::byte 类型。
似乎很明显,对象s 正在访问其存储的值。
项目符号中列出的类型是进行访问的泛左值的类型,而不是正在访问的对象的类型。在这段代码中,glvalue 类型是int,它不是聚合或联合类型,排除了 11.6。
我的问题是:这段代码是否正确,如果正确,在上述哪个要点下是允许的?
【问题讨论】:
-
我主要熟悉 C 标准而不是 C++ 标准,但前者的作者认为没有必要指定 any 左值的情况聚合成员类型实际上可用于访问聚合。即使像
myStruct.member=23;这样的东西也会调用UB,除非member具有字符类型,但是编译器必须相当迟钝才能识别出这种用法。编译器同样必须是迟钝的,无法识别新转换为成员类型的指针用于访问该成员的情况。然而,标准...... -
...不强制要求这种行为,但依赖于编译器编写者认识到即使在标准未强制要求的情况下,质量实现也应该表现得有用。不幸的是,这种依赖被证明是错误的。
标签: c++ language-lawyer strict-aliasing reinterpret-cast