M.M*_*M.M 23 c++ strict-aliasing language-lawyer reinterpret-cast
示例代码:
struct S { int x; };
int func()
{
S s{2};
return (int &)s; // Equivalent to *reinterpret_cast<int *>(&s)
}
Run Code Online (Sandbox Code Playgroud)
我认为这很常见,被认为是可以接受的.该标准确保结构中没有初始填充.但是,这种情况未在严格别名规则(C++ 17 [basic.lval]/11)中列出:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- (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的类型,而不是被访问对象的类型.在这段代码中,glvalue类型int不是聚合类型或联合类型,排除了11.6.
我的问题是:这个代码是否正确,如果是,那么允许上述哪一个要点?
M.M*_*M.M 16
演员的行为归结为[expr.static.cast]/13;
"到指针类型的prvalue CV1
void"可以被转换成类型的prvalue"指针CV2T",其中T是一个对象类型和CV2是相同的CV-资格,或更大的CV-资格比,CV1.如果原始指针值表示A内存中字节的地址并且A不满足对齐要求T,则未指定结果指针值.否则,如果原始指针值指向一个对象a,并且存在一个b类型T(忽略cv-qualification)的对象,该对象是指针可互换的a,则结果是指向的对象b.否则,转换指针值不变.
指针可互换的定义是:
如果出现以下情况,则两个对象a和b是指针可互换的:
- 它们是同一个对象,或者
- 一个是union对象,另一个是该对象的非静态数据成员,或
- 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则该对象的第一个基类子对象,或者
- 存在对象c,使得a和c是指针可互换的,并且c和b是指针可互换的.
所以在原始代码中,s并且s.x是指针可互换的,并且它遵循(int &)s实际指定s.x.
所以,在严格别名规则,其存储的值被访问的对象s.x,而不是s等有没有问题,代码是正确的.
我认为它在expr.reinterpret.cast#11中
如果可以使用reinterpret_cast 将"指向T1的指针"类型的表达式显式转换为"指向T2的指针"
x类型,则可以将指定对象的类型T1的glvalue表达式强制转换为"对T2的引用".结果是即 其中是一个指向型的"指针T1".没有创建临时,没有复制,也没有调用构造函数或转换函数[1].*reinterpret_cast<T2 *>(p)px
[1] 当结果引用与源glvalue相同的对象时,这有时被称为类型双关语
支持@ MM关于指针不可挽回的答案:
来自cppreference:
假设对齐要求均得到满足,
reinterpret_cast并 没有改变的外部指针的值数有限的情况下,处理指针相互转换的对象:
struct S { int a; } s;
int* p = reinterpret_cast<int*>(&s); // value of p is "pointer to s.a" because s.a
// and s are pointer-interconvertible
*p = 2; // s.a is also 2
Run Code Online (Sandbox Code Playgroud)
与
struct S { int a; };
S s{2};
int i = (int &)s; // Equivalent to *reinterpret_cast<int *>(&s)
// i doesn't change S.a;
Run Code Online (Sandbox Code Playgroud)