[英]Why introduce `std::launder` rather than have the compiler take care of it?
我刚读过
坦率地说,我只是摸不着头脑。
让我们从@NicolBolas 接受的答案中的第二个例子开始:
aligned_storage<sizeof(int), alignof(int)>::type data; new(&data) int; int *p = std::launder(reinterpret_cast<int*>(&data));
[basic.life]/8 告诉我们,如果你在旧的存储中分配一个新的 object,你不能通过指向旧的指针访问新的 object。
std::launder
允许我们避开它。
那么,为什么不直接更改语言标准,以便通过reinterpret_cast<int*>(&data)
访问data
是有效/适当的呢? 在现实生活中,洗钱是一种向法律隐瞒现实的方式。 但我们没有什么可隐瞒的——我们正在做一些完全合法的事情。 那么,当编译器注意到我们正在以这种方式访问data
时,为什么它不能将其行为更改为其std::launder()
行为呢?
关于第一个例子:
X *p = new (&u.x) X {2};
因为 X 是微不足道的,我们不需要在创建新的 object 之前销毁旧的 object,所以这是完全合法的代码。 新的 object 的 n 成员将为 2。
那么告诉我...
uxn
会返回什么?显而易见的答案是 2。但这是错误的,因为允许编译器假定一个真正的
const
变量(不仅仅是一个 const&,而是一个声明为const
的 object 变量)永远不会改变。 但我们只是改变了它。
那么为什么不让编译器不允许我们在写这种通过指针访问常量字段的代码时做出假设呢?
为什么使用这个伪函数来在形式语言语义中打个洞是合理的,而不是根据代码是否执行这些示例中的操作来将语义设置为它们需要的样子?
取决于代码是否在这些示例中执行类似操作
因为编译器不能总是知道何时“以这种方式”访问data
。
按照目前的情况,允许编译器假设以下代码:
struct foo{ int const x; };
void some_func(foo*);
int bar() {
foo f { 123 };
some_func(&f);
return f.x;
}
bar
将始终返回 123。编译器可能会生成实际访问 object 的代码。 但是 object model不需要这个。 fx
是const
object (不是const
的引用/指针),因此不能更改。 并且f
必须始终命名相同的 object (实际上,这些是您必须更改的标准部分)。 因此, fx
的值不能通过任何非 UB 方式改变。
为什么有这个伪函数在形式语言语义上打一个洞是合理的
这其实是讨论过的。 该论文提出了这些问题已经存在多长时间(即:自 C++03 以来),并且经常使用 object model 实现的优化。
该提案被拒绝,理由是它实际上无法解决问题。 从这次旅行报告中:
然而,在讨论过程中发现,提议的替代方案无法处理所有受影响的场景(特别是 vtable 指针正在发挥作用的场景),并且没有获得共识。
该报告没有就此事提供任何具体细节,相关讨论也未公开。 但是提案本身确实指出它不允许对第二个虚拟 function 调用进行去虚拟化,因为第一个调用可能已经构建了一个新的 object。 因此,即使是 P0532 也不会使launder
变得不必要,只是不太必要。
我采取相反的方法。 我不喜欢 c++ 像这样玩弄 memory 或者至少允许你玩。 当尝试创建新的 object 和包含常量值或 object 但具有 const 成员的地址时,编译器应抛出错误。 如果程序员说 const 就是 const。 对不起。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.