std :: launder的目的是什么?

时间:2016-09-08 04:16:02

标签: c++ c++17 stdlaunder

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

本文解决的问题是什么?我必须注意哪些语言的变化?我们launder是什么?

2 个答案:

答案 0 :(得分:204)

std::launder恰当地命名,但前提是你知道它的用途。它执行内存清洗

考虑一下文中的例子:

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

U u = {{ 1 }};

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

由于nconst变量,编译器可以自由地假设u.x.n 总是为1。

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

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

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

那么告诉我...... u.x.n会回来什么?

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

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

那么......我们怎样才能恰当地谈论u.x.n

我们必须清洗记忆:

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允许我们支持这一点。

答案 1 :(得分:3)

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

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

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

Why introduce `std::launder` rather than have the compiler take care of it?

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