为什么要引入`std::launder`而不是让编译器来处理它?
我刚读完
std::launder 的目的是什么?
坦率地说,我只能挠头。
让我们从@NicolBolas 接受的答案中的第二个例子开始:
aligned_storage<sizeof(int), alignof(int)>::type data; new(&data) int; int *p = std::launder(reinterpret_cast<int*>(&data));[basic.life]/8 告诉我们,如果在旧对象的存储中分配新对象,则无法通过指向旧对象的指针访问新对象。
std::launder允许我们回避这一点。
那么,为什么不改变语言标准,以便data通过 a访问reinterpret_cast<int*>(&data)是有效/适当的?在现实生活中,洗钱是一种向法律隐瞒现实的方式。但我们没有什么可隐瞒的——我们在这里做的事情完全合法。那么当编译器std::launder()注意到我们正在以data这种方式访问时,为什么不能将其行为更改为它的行为呢?
继续第一个例子:
X *p = new (&u.x) X {2};因为 X 是微不足道的,我们不需要在创建一个新对象之前销毁旧对象,所以这是完全合法的代码。新对象的 n 成员将为 2。
所以告诉我……什么会
u.x.n回来?显而易见的答案是 2。但这是错误的,因为允许编译器假设一个真正的
const变量(不仅仅是一个 const&,而是一个声明的对象变量const)永远不会改变。但我们只是改变了它。
那么,为什么不使编译器不能被允许作一个假设,当我们写这样的代码,通过该指针访问恒场?
为什么使用这个伪函数来在形式语言语义中打一个洞是合理的,而不是根据代码是否像这些示例中那样将语义设置为它们需要的样子?
回答
取决于代码是否执行这些示例中的操作
因为编译器不能总是知道何时data以“这种方式”访问。
按照目前的情况,编译器可以假设,对于以下代码:
struct foo{ int const x; };
void some_func(foo*);
int bar() {
foo f { 123 };
some_func(&f);
return f.x;
}
bar将始终返回 123。编译器可能会生成实际访问该对象的代码。但是对象模型不需要这个。f.x是一个const对象(不是指向 的引用/指针const),因此无法更改。并且f需要始终命名相同的对象(实际上,这些是您必须更改的标准部分)。因此,f.x任何非 UB 手段都无法更改的值。
为什么用这个伪函数在形式语言语义上打一个洞是合理的
这其实是讨论过的。那篇论文提出了这些问题存在多长时间(即:自 C++03 以来),并且经常使用该对象模型实现的优化。
该提案被拒绝,理由是它实际上并不能解决问题。从这次旅行报告:
然而,在讨论过程中发现,提议的替代方案无法处理所有受影响的场景(尤其是 vtable 指针起作用的场景),并且没有获得共识。
该报告没有对此事进行任何具体细节,相关讨论也未公开。但是提案本身确实指出它不允许对第二个虚函数调用进行去虚拟化,因为第一个调用可能已经构建了一个新对象。所以即使是 P0532 也不会变得launder不必要,只是不太必要。
- @einpoklum Modifying `f.x` during the lifetime of `x` (via a `const_cast`) is UB per [[dcl.type.cv]/4](https://timsong-cpp.github.io/cppwp/n4659/dcl.type.cv#4). Ending the lifetime of `x` and/or `f` within `some_func` would be allowed on its own, but then that would cause the sequenced-after access in `bar` to be UB by [[basic.life]/(8.3)](https://timsong-cpp.github.io/cppwp/n4659/basic.life#8).
- re. your second comment, compilers missing an optimization is hardly grounds for suspicion of the standard. Other reasons for missed optimizations include: nobody ever put in work to add this case to the optimizer; and the userbase expecting non-standard behaviour. [Similar case](https://godbolt.org/z/8Ezs9f) without the struct gets optimized; structs with `const` members are uncommon