Adr*_*ian 10 c++ placement-new language-lawyer c++11
当在实例emplace_back()上调用时std::vector,会在先前分配的存储中创建一个对象。这可以通过 Placement-new 轻松实现,它非常便携。但现在,我们需要访问嵌入的元素而不调用未定义的行为。
从这篇文章中 我了解到有两种方法可以做到这一点
使用placement-new返回的指针:
auto *elemPtr = new (bufferPtr) MyType();
或者,从 C++17 开始,std::launder所转换的指针bufferPtr
auto *elemPtr2 = std::launder(reinterpret_cast<MyType*>(bufferPtr));
第二种方法可以很容易地推广到这样的情况,即我们有很多对象放置在相邻的内存位置,如std::vector. 但在 C++17 之前人们做了什么?一种解决方案是将placement-new 返回的指针存储在单独的动态数组中。虽然这当然是合法的,但我认为它并没有真正实现 std::vector [此外,单独存储我们已经知道的所有地址是一个疯狂的想法]。另一个解决方案是存储lastEmplacedElemPtrinside std::vector,并从中删除适当的整数 - 但由于我们实际上没有MyType对象数组,这可能也是未定义的。事实上,此 cppreference 页面中的一个示例声称,如果我们有两个相同类型的指针比较相等,并且其中一个可以安全地取消引用,则取消引用另一个可能仍然是未定义的。
那么,在 C++17 之前有没有办法以可移植的方式实现 std::vector 呢?或者也许 std::launder 确实是 C++ 的一个关键部分,当涉及到新的放置时,自 C++98 以来就缺失了?
我知道这个问题表面上与SO上的许多其他问题相似,但据我所知,他们都没有解释如何合法地迭代由placement-new构造的对象。事实上,这一切都有点令人困惑。例如,示例形式的cppreference 文档中的 std::aligned_storage注释
似乎表明 C++11 和 C++17 之间存在一些变化,并且简单的别名违规reinterpret_cast在 C++17 之前是合法的 [无需std::launder]。类似地,在文档 的示例中,std::malloc
他们只是对返回的指针进行指针算术std::malloc(static_cast在正确的类型之后)。
相比之下,根据这个SO问题的答案 ,当涉及到placement-new和reinterpret_cast:
自 C++11(特别是 [basic.life])以来,出现了一些重要的规则澄清。但规则背后的意图并没有改变。
P0593R6和P1971R0/RU007之后的 IIUC都合并到 C++20 中,并且与先前修订版的缺陷报告相当可观,因此std::vector不需要可移植的实现std::launder。
首先,在allocate给定分配器的函数(可能调用operator new、std::malloc或类似的函数)返回后,在分配的存储中隐式创建一个value_type[N]数组(其中N等于传递给 的请求数allocate)(感谢 P0593R6),因此指针算术有效。即使元素可能是未构造的。
其次,当我们使用不带的placement-new时std::launder,构造的对象可以被视为数组元素,因为即使元素类型具有 const/reference 非静态数据成员,新对象也会透明地替换数组元素(感谢 P1971R0/RU007 和后续)在P2103R0/US041中修复)。
| 归档时间: |
|
| 查看次数: |
486 次 |
| 最近记录: |