且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

std::launder 的目的是什么?

更新时间:2023-11-12 23:23:46

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

std::launder is aptly named, though only if you know what it's for. It performs memory laundering.

考虑论文中的例子:

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

U u = {{ 1 }};

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

That statement performs aggregate initialization, initializing the first member of U with {1}.

因为 n 是一个 const 变量,编译器可以***地假设 uxn 应该 总是 为 1.

Because n is a const variable, the compiler is free to assume that u.x.n shall always be 1.

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

So what happens if we do this:

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

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

Because X is trivial, we need not destroy the old object before creating a new one in its place, so this is perfectly legal code. The new object will have its n member be 2.

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

So tell me... what will u.x.n return?

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

The obvious answer will be 2. But that's wrong, because the compiler is allowed to assume that a truly const variable (not merely a const&, but an object variable declared const) will never change. But we just changed it.

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

[basic.life]/8 spells out the circumstances when it is OK to access the newly created object through variables/pointers/references to the old one. And having a const member is one of the disqualifying factors.

那么……我们如何正确地谈论u.x.n?

So... how can we talk about u.x.n properly?

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

We have to launder our memory:

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

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

Money laundering is used to prevent people from tracing where you got your money from. Memory laundering is used to prevent the compiler from tracing where you got your object from, thus forcing it to avoid any optimizations that may no longer apply.

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

Another of the disqualifying factors is if you change the type of the object. std::launder can help here too:

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

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

[basic.life]/8 tells us that, if you allocate a new object in the storage of the old one, you cannot access the new object through pointers to the old. launder allows us to side-step that.