Oli*_*liv 8 c++ pointers undefined-behavior language-lawyer c++17
考虑这个联合:
union A{
int a;
struct{
int b;
} c;
};
Run Code Online (Sandbox Code Playgroud)
c并且a不是布局兼容类型,因此无法读取bthrough 的值a:
A x;
x.c.b=10;
x.a+x.a; //undefined behaviour (UB)
Run Code Online (Sandbox Code Playgroud)
对于审判1和审判2,请参阅此问题
现在让我们使用std::launder它似乎不想要的东西:
A x;
x.a=10;
auto p = &x.a; //(1)
x.c.b=12; //(2)
p = std::launder(p); //(2')
*p+*p; //(3) UB?
Run Code Online (Sandbox Code Playgroud)
可以std::launder改变什么吗?根据[ptr.launder]:
template <class T> constexpr T* launder(T* p) noexcept;需要:
p表示内存中一个字节的地址A。在其生存期内且类型相似的对象XT位于地址A处。通过结果可以访问的所有存储字节都是可以访问的p(请参见下文)。返回:
T *指向X的类型的值。备注:只要可以在核心常量表达式中使用其参数的值,就可以在核心常量表达式中使用此函数的调用。通过一个指向对象Y的指针值可以到达一个存储字节,如果该对象位于Y占用的存储空间内,则该对象可以与Y进行指针可互转换,如果Y是数组元素,则可以立即封闭数组对象。如果T是函数类型或cv void,则程序格式错误。
粗体字强调了困扰我的东西。如果p是无效的指针值,那么如何访问任何字节的存储空间?另一方面,这样的阅读std::launder是不可用的。
否则,可能p的在(2)的值是代表存储的区域作为在‘注意’的谈及的指针值[basic.life] :
如果不满足这些条件,则可以通过调用
std?::?launder([support.dynamic])从表示其存储地址的指针获取指向新对象的指针。
注释中明确允许使用它。
basic.life包含以下使std::launder不必要的规则:
如果在对象的生存期结束之后并且在重新使用或释放该对象占用的存储空间之前,在原始对象占用的存储位置上创建了一个新对象,则指向原始对象的指针,引用原始对象,否则原始对象的名称将自动引用新对象,并且在新对象的生命周期开始后,可以用于操作新对象,如果:
- 新对象的存储空间正好覆盖了原始对象所占据的存储位置,并且
- 新对象与原始对象具有相同的类型(忽略顶级cv限定词),并且
- 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为const限定的非静态数据成员或引用类型,并且
- 原始对象和新对象都不是潜在重叠的子对象。
[注:如果不满足这些条件,则可以通过调用从表示其存储地址的指针获取指向新对象的指针
std::launder。-?尾注?]
这种“新对象是在原始对象所占的存储位置创建的”案例在此明确适用,因为:
满足所有项目符号条件,因为“潜在重叠的子对象”是指基类子对象,而联合成员不是。(在您链接的版本中,该项目符号直接提到了基类子对象。)
但是,即使此解释将改变为反对工会,该注释也特别提到std::launder绕过此限制。
请注意,较早版本的Standard将该子对象排除在此规则之外...但是该注释清楚地表明,它也std::launder将绕过该问题。
| 归档时间: |
|
| 查看次数: |
270 次 |
| 最近记录: |