在 C++20 之前,使用 const 或引用字段在“A”上调用“std::vector::data()”

2023-12-24

这是答案的后续在具有参考字段的类中放置新内容 https://stackoverflow.com/questions/62158678/placement-new-on-a-class-with-reference-field.

Calling std::vector<A>::data()按类型A具有参考 or constfields,返回一个指向对象的指针,该对象可以通过放置 new 来通过原始向量进行更改,这会导致 const 或引用字段原始对象被替换,同时仍然由另一个指针管理,通过调用返回data().

例如:

struct A {
    const int i = 0;
};

int main() {
    std::vector<A> vec = {{1}, {2}};
    auto ptr = vec.data();
    std::cout << ptr[1].i << std::endl; // 2
    vec.pop_back();
    vec.push_back({3}); // placement new, inside
    std::cout << ptr[1].i << std::endl; // 3
}

C++17 试图通过引入来解决此类问题std::launder但后来大家一致认为,虽然std::launder可能会解决其他问题,但它并不能真正解决上述用例的问题,如中所述NB US042 https://github.com/cplusplus/nbballot/issues/7.

一些问题 - 对于 C++ 版本C++20 之前:

  • Since NB US042被接受为C++20 规范的更改 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1971r0.html#RU007,但未标记为 DR - 请建议,仅根据规格,避免使用std::vector<A>::data()在一个类型上A具有引用或 const 字段,如上例所示?

  • 或者,规范的措辞std::vector<>::data()涵盖了,使其合法化并将可实施性问题留给库实施者?

  • 如果是后者,图书馆能做什么?使其合法化?

  • 如果它不能真正做任何有用的事情使其合法化是C++20之前的UB吗?

  • 如果它是 C++20 之前的 UB,为什么这个更改不被视为 DR 的候选者,与p0593r6 https://wg21.link/p0593r6?无论如何,编译器很可能会做正确的事情,为什么不追溯性地强制执行呢?


这段代码是否有效取决于实现是否有严格的指针安全,C++20 中删除的概念。

简单来说就是指针ptr对于 [ 范围内的算术有效ptr, ptr+2) 调用后立即data().

拨打电话后pop_back(),任何保存的指向索引 1 的指针肯定是无效的,因为迭代器失效规则std::vector<T>::pop_back() https://eel.is/c++draft/sequences#vector.modifiers-3

效果:在擦除点或擦除点之后使迭代器和引用无效。

拨打电话后pop_back(), 指针ptr先前获得的对于其原始范围内的算术不再有效(用它来计算ptr+1不再产生“安全派生”的指针值)。

拨打电话后push_back(),算术使用的严格安全性ptr没有恢复。但是,使用原始索引ptr,尚未被无效化pop_back()(仅减少了可达范围),在具有“宽松指针安全性”的实现中仍然允许。也就是说,表达式ptr+1 and ptr[1]涉及有效但不安全派生的指针值。

一个新的电话data()返回一个指针值,该值与原始值进行比较,但与旧的保存指针不同,它可以用于安全地导出向量当前长度的值。同样,具有宽松指针有效性的实现并不关心。


对 C++20 对具有 const 成员的对象的生命周期所做的更改在这里不起作用,因为使用现有指针来引用替换对象不仅仅被禁止[basic.life]而且还可以通过迭代器失效子句pop_back.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 C++20 之前,使用 const 或引用字段在“A”上调用“std::vector::data()” 的相关文章

随机推荐