繁体   English   中英

std::launder 的目的是什么?

[英]What is the purpose of std::launder?

P0137引入了函数模板std::launder并对有关联合、生命周期和指针的部分中的标准进行了很多很多更改。

这篇论文要解决什么问题? 我必须注意语言的哪些变化? 我们在launder什么?

std::launder的命名恰如其分,但std::launder是您知道它的用途。 它执行内存清洗

考虑论文中的例子:

struct X { const int n; };
union U { X x; float f; };
...

U u = {{ 1 }};

该语句执行聚合初始化,使用{1}初始化U的第一个成员。

因为n是一个const变量,编译器可以自由地假设uxn始终为 1。

那么如果我们这样做会发生什么:

X *p = new (&u.x) X {2};

因为X是微不足道的,我们不需要在创建一个新对象之前销毁旧对象,所以这是完全合法的代码。 新对象的n成员将为 2。

所以告诉我... uxn返回什么?

显而易见的答案是 2。但这是错误的,因为编译器被允许假设一个真正的const变量(不仅仅是一个const& ,而是一个声明为const的对象变量)永远不会改变 但我们只是改变了它。

[basic.life]/8阐明了可以通过变量/指针/对旧对象的引用访问新创建的对象的情况。 拥有const成员是取消资格的因素之一。

那么……我们怎样才能正确地谈论uxn呢?

我们必须清洗我们的记忆:

assert(*std::launder(&u.x.n) == 2); //Will be true.

洗钱用于防止人们追踪您的资金来源。 内存清洗用于防止编译器跟踪您从何处获取对象,从而迫使它避免任何可能不再适用的优化。

另一个不合格的因素是您是否更改了对象的类型。 std::launder也可以在这里提供帮助:

aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));

[basic.life]/8告诉我们,如果在旧对象的存储中分配新对象,则无法通过指向旧对象的指针访问新对象。 launder使我们能够launder这一点。

std::launder是一个误称。 此函数执行清洗相反的操作:它污染指向内存,以消除编译器可能对指向值的任何期望。 它排除了基于此类期望的任何编译器优化。

因此,在@NicolBolas 的回答中,编译器可能会假设某些内存保存一些常量值; 或未初始化。 你告诉编译器:“那个地方(现在)脏了,不要做出这样的假设”。

如果您想知道为什么编译器一开始总是坚持其幼稚的期望,并且需要您明显地为它弄脏东西 - 您可能想阅读以下讨论:

为什么要引入 `std::launder` 而不是让编译器来处理它?

...这使我对std::launder含义std::launder这种看法。

我认为std::launder有两个目的。

  1. 一个障碍不断折叠/传播,包括去虚拟化。
  2. 基于细粒度对象结构的别名分析的障碍。

过度激进的恒定折叠/传播的障碍(已放弃)

从历史上看,C++ 标准允许编译器假设以某种方式获得的 const 限定或引用非静态数据成员的值是不可变的,即使其包含的对象是非常量的并且可以通过放置 new 重用。

在 C++17/ P0137R1 中std::launder被引入作为禁用上述(错误)优化( CWG 1776 )的功能,这是std::optional所需要的。 正如P0532R0 中所讨论的, std::vectorstd::deque可移植实现可能也需要std::launder ,即使它们是 C++98 组件。

幸运的是, RU007 (包含在P1971R0和 C++20 中)禁止这种(错误)优化。 AFAIK 没有编译器执行此(错误)优化。

去虚拟化的障碍

虚拟表指针 (vptr) 在其包含的多态对象的生命周期内可以被视为常量,这是去虚拟化所需的。 鉴于 vptr 不是非静态数据成员,编译器仍然允许基于 vptr 没有改变的假设执行去虚拟化(即,对象仍然在其生命周期中,或者它被新的对象重用)相同的动态类型)在某些情况下。

对于一些不寻常的用途具有不同的动态类型(示出的新对象替换一个多态对象这里), std::launder需要作为devirtualization的屏障。

IIUC Clang 使用这些语义 ( LLVM-D40218 ) 实现了std::launder ( __builtin_launder )。

基于对象结构的别名分析的障碍

P0137R1还通过引入指针互转换来改变 C++ 对象模型。 IIUC 此类更改使N4303 中提出的某些“基于对象结构的别名分析”成为可能

因此,P0137R1 直接使用从未定义的unsigned char [N]数组中取消引用reinterpret_cast的指针,即使该数组为另一个正确类型的对象提供存储。 然后需要std::launder来访问嵌套对象。

这种别名分析似乎过于激进,可能会破坏许多有用的代码库。 AFAIK 它目前没有被任何编译器实现。

与基于类型的别名分析/严格别名的关系

IIUC std::launder和基于类型的别名分析/严格别名无关。 std::launder要求在提供的地址上有一个正确类型的活对象。

然而,它们似乎在 Clang ( LLVM-D47607 ) 中意外地相关。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM