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

ein*_*ica 17 c++ rationale language-lawyer c++17 stdlaunder

我刚读完

std::launder 的目的是什么?

坦率地说,我只能挠头。

让我们从@NicolBolas 接受的答案中的第二个例子开始:

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

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

那么,为什么不改变语言标准,以便data通过 a访问reinterpret_cast<int*>(&data)是有效/适当的?在现实生活中,洗钱是一种向法律隐瞒现实的方式。但我们没有什么可隐瞒的——我们在这里做的事情完全合法。那么当编译器std::launder()注意到我们正在以data这种方式访问时,为什么不能将其行为更改为它的行为呢?

继续第一个例子:

X *p = new (&u.x) X {2};
Run Code Online (Sandbox Code Playgroud)

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

所以告诉我……什么会u.x.n回来?

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

那么,为什么不使编译器不能被允许作一个假设,当我们写这样的代码,通过该指针访问恒场?

为什么使用这个伪函数来在形式语言语义中打一个洞是合理的,而不是根据代码是否像这些示例中那样将语义设置为它们需要的样子?

Nic*_*las 7

取决于代码是否执行这些示例中的操作

因为编译器不能总是知道何时data以“这种方式”访问。

按照目前的情况,编译器可以假设,对于以下代码:

struct foo{ int const x; };

void some_func(foo*);

int bar() {
    foo f { 123 };
    some_func(&f);
    return f.x;
}
Run Code Online (Sandbox Code Playgroud)

bar将始终返回 123。编译器可能会生成实际访问该对象的代码。但是对象模型不需要这个。f.x是一个const对象(不是指向 的引用/指针const),因此无法更改。并且f需要始终命名相同的对象(实际上,这些是您必须更改的标准部分)。因此,f.x任何非 UB 手段都无法更改的值。

为什么用这个伪函数在形式语言语义上打一个洞是合理的

其实是讨论过的。那篇论文提出了这些问题存在多长时间(即:自 C++03 以来),并且经常使用该对象模型实现的优化。

该提案被拒绝,理由是它实际上并不能解决问题。从这次旅行报告

然而,在讨论过程中发现,提议的替代方案无法处理所有受影响的场景(尤其是 vtable 指针起作用的场景),并且没有获得共识。

该报告没有对此事进行任何具体细节,相关讨论也未公开。但是提案本身确实指出它不允许对第二个虚函数调用进行去虚拟化,因为第一个调用可能已经构建了一个新对象。所以即使是 P0532 也不会变得launder不必要,只是不太必要。

  • @einpoklum 在 `x` 的生命周期内修改 `fx` (通过 `const_cast`)是 UB per [\[dcl.type.cv\]/4](https://timsong-cpp.github.io/cppwp /n4659/dcl.type.cv#4)。在“some_func”中结束“x”和/或“f”的生命周期本身是允许的,但这会导致“bar”中的后序访问被[\[basic.life\] UB /(8.3)](https://timsong-cpp.github.io/cppwp/n4659/basic.life#8)。 (4认同)
  • 我认为“const subobject”的示例不再相关:https://github.com/cplusplus/draft/commit/fd8ff6441f93024bd0ee6e03a03c08be8e1b5ce0 (4认同)
  • 关于。您的第二条评论是,编译器缺少优化几乎没有理由怀疑该标准。错过优化的其他原因包括: 没有人投入精力将这种情况添加到优化器中;以及期望非标准行为的用户群。[类似情况](https://godbolt.org/z/8Ezs9f) 没有结构得到优化;具有“const”成员的结构并不常见 (2认同)
  • @einpoklum:“*我的意思是,const_cast 后跟 fx* 的修改”[dcl.type.cv]/4 对于 `const_cast` 的使用没有例外。对象是否 * 是 const * 与对象的引用是否是 const 不是同一个问题。声明为 const 的对象是一个 `const` 对象,并且根据 [dcl.type.cv]/4,您所做的任何会导致其值被修改的操作都会自动成为 UB。 (2认同)
  • 很公平。但考虑到这种情况,我想说“std::launder”与恰当命名相反。`std::soil` 会更合适。 (2认同)