问题描述
考虑这个联合:
union A{
int a;
struct{
int b;
} c;
};
c
和a
不是布局兼容类型,因此无法通过a
读取b
的值:
c
and a
are not layout-compatibles types so it is not possible to read the value of b
through a
:
A x;
x.c.b=10;
x.a+x.a; //undefined behaviour (UB)
现在让我们使用std::launder
来实现其预期之外的功能:
Now let's use std::launder
for what it does not seems to be intended:
A x;
x.a=10;
auto p = &x.a; //(1)
x.c.b=12; //(2)
p = std::launder(p); //(2')
*p+*p; //(3) UB?
std::launder
可以更改任何内容吗?根据 [ptr.launder] :
Could std::launder
change anything? According to [ptr.launder]:
要求:p
表示内存中字节的地址 A .在其生存期内且类型类似于T
的对象 X 位于地址 A 中.通过结果可以访问的所有存储字节都可以通过p
访问(见下文).
Requires: p
represents the address A of a byte in memory. An object X that is within its lifetime and whose type is similar to T
is located at the address A. All bytes of storage that would be reachable through the result are reachable through p
(see below).
返回:类型为T *
的值指向 X .
备注:只要在核心常量表达式中使用其参数的值,就可以在核心常量表达式中使用此函数的调用. 通过指向对象Y的指针值可以访问一个字节的存储空间,如果它位于Y占用的存储空间,可以与Y进行指针互换的对象或立即封闭的数组对象中如果Y是数组元素.如果T为函数类型或cv void,则程序格式错误.
Remarks: An invocation of this function may be used in a core constant expression whenever the value of its argument may be used in a core constant expression. A byte of storage is reachable through a pointer value that points to an object Y if it is within the storage occupied by Y, an object that is pointer-interconvertible with Y, or the immediately-enclosing array object if Y is an array element. The program is ill-formed if T is a function type or cv void.
粗体字强调了困扰我的东西.如果p
是无效的指针值,那么如何访问任何存储字节?另一方面,std::launder
只是不可用.
The bolded sentence emphasizes a someting that troubles me. If p
is an invalid pointer value, how could any byte of storage be accessible? On the other hand with such a reading std::launder
is just not usable.
否则,可以将(c)中的p
值用作表示存储区域的指针值,如 [basic.life] :
Otherwise, could p
's value at (2) be a pointer value that represents a region of storage as is talked about in a "Note" in [basic.life]:
推荐答案
注释中允许明确.
basic.life
包含以下规则,从而使std::launder
不再必要:
basic.life
contains the following rule that makes std::launder
unnecessary:
- 新对象的存储空间正好覆盖了原始对象所占据的存储位置,并且
- 新对象与原始对象具有相同的类型(忽略顶级cv限定词),并且
- 原始对象的类型不是const限定的,并且,如果是类类型,则不包含任何类型为const限定的非静态数据成员或引用类型,并且
- 原始对象和新对象都不是潜在重叠的子对象.
- the storage for the new object exactly overlays the storage location which the original object occupied, and
- the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
- the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
- neither the original object nor the new object is a potentially-overlapping subobject.
[注:如果不满足这些条件,则可以通过调用std::launder
从表示其存储地址的指针中获取指向新对象的指针. —尾注]
[ Note: If these conditions are not met, a pointer to the new object can be obtained from a pointer that represents the address of its storage by calling std::launder
. — end note ]
这种新对象是在原始对象所占的存储位置创建的"案例在此明确适用,因为:
This "new object is created at the storage location which the original object occupied" case clearly applies here because:
所有子弹条件都得到满足,例如潜在重叠的子对象" 引用基类的子对象,而联合成员不是. (在您链接的版本中,该项目符号直接提到了基类子对象.)
All the bullet conditions are met, as "potentially overlapping subobject" refers to base class subobjects, which union members are not. (And in the version you linked to, that bullet mentioned base class subobjects directly.)
然而,即使这种解释将针对工会而改变,但该注释仍特别提及std::launder
绕过了这一限制.
However even if this interpretation were to change against unions, the note specifically mentions that std::launder
bypasses this restriction.
请注意,较早版本的Standard将该子对象排除在此规则之外...但是注释清楚表明,std::launder
也将绕过该问题.
Do note that older versions of the Standard excluded subobjects from this rule... but the note makes it clear that std::launder
would have bypassed that problem as well.
这篇关于使用std :: launder获取指向活动联合成员的指针,而不是指向非活动联合成员的指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!