重新分配 std::vector 对象时指向内部数据结构的指针的有效性 [英] Validity of pointers to internal data structure when reallocating a std::vector object

查看:28
本文介绍了重新分配 std::vector 对象时指向内部数据结构的指针的有效性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设有一个包含整数向量的类 A.现在假设创建了一个 A 向量.例如,如果由于 push_back 发生了 A 对象的重新分配(因此向量对象被移动),指向 int 本身的指针是否仍然有效?这能保证吗?

Assume there is a class A that contains a vector of ints. Now assume that a vector of A's is created. If a reallocation of an A object occurs (so the vector object is moved) due to a push_back for example, will pointers to the ints themselves remain valid? Is this guaranteed?

澄清:

class A {
public:
    A() {};
    std::vector<int> a = {1,2,3,4,5,6,7,8,9};
};

int main()
{
    std::vector<A> aVec(2);

    int *x1 = &(aVec[1].a[2]);
    A *x2 = &aVec[1];
    std::cout << x1 << " - " << x2 << " - " << aVec.capacity() << "\n";

    aVec.resize(30);

    int *y1 = &(aVec[1].a[2]);
    A *y2 = &aVec[1];
    std::cout << y1 << " - " << y2 << " - " << aVec.capacity() << "\n";
}

运行此代码给出:

0x1810088 - 0x1810028 - 2
0x1810088 - 0x18100c8 - 30

所以它表明指针仍然有效.但我想确保这是有保证的,而不仅仅是偶然的事情.我倾向于说这是有保证的,因为向量的内部数据是动态分配的,但同样,只是想检查一下.

so it shows that the pointers remain valid. But I want to make sure that this is guaranteed and not just a chance thing. I'm leaning towards saying that this is guaranteed since the vector's internal data is dynamically allocated, but, again, just wanted to check.

我看过这里 [ 迭代器失效规则 ] 但它没有考虑这种特定情况(即重新分配矢量对象本身).

I have looked here [ Iterator invalidation rules ] but it doesn't consider this specific case (i.e. reallocation of the vector object itself).

更新:

我试着用这个来检查我在 Jarod42 回答的评论中写了什么:

I tried this to check what I wrote in the comments for Jarod42's answer:

std::vector<std::vector<int>> aVec(2, {1,2,3});

int *x1 = &(aVec[1][2]);
std::vector<int> *x2 = &aVec[1];
std::cout << x1 << " - " << x2 << " - " << aVec.capacity() << "\n";

aVec.resize(30);

int *y1 = &(aVec[1][2]);
std::vector<int> *y2 = &aVec[1];
std::cout << y1 << " - " << y2 << " - " << aVec.capacity() << "\n";

得到这个:

0x16f0098 - 0x16f0048 - 2
0x16f0098 - 0x16f00c8 - 30

这对我来说很奇怪.我期望 x2==y2.

which is strange to me. I expected x2==y2.

推荐答案

不幸的是,这不能保证.也就是说,所有 3 个当前实现(libc++、libstdc++ 和 VS-2015)似乎都保证了这一点.问题是 A 的移动构造函数是否为 noexcept:

Unfortunately this is not guaranteed. That being said, it is the case that all 3 current implementations (libc++, libstdc++ and VS-2015) appear to guarantee it. The question is whether or not the move constructor for A is noexcept:

static_assert(std::is_nothrow_move_constructible<A>::value, "");

A 的移动构造函数是编译器提供的,因此依赖于 std::vector 的移动构造函数.如果std::vector的移动构造函数是noexcept,则A的移动构造函数是noexcept,否则不是.

The move constructor for A is compiler supplied, and thus dependent upon the move constructor of std::vector<int>. If the move constructor of std::vector<int> is noexcept, then the move constructor for A is noexcept, else it is not.

当前草案 N4296 没有将 vector 的移动构造函数标记为 noexcept.但是它允许实现这样做.

The current draft N4296 does not mark the move constructor for vector as noexcept. However it allows implementations to do so.

这一行:

aVec.resize(30);

如果移动构造函数是noexcept,将使用A 的移动构造函数,否则它将使用A 的复制构造函数.如果它使用 A 的复制构造函数,则整数的位置会改变.如果它使用 A 的移动构造函数,则整数的位置将保持稳定.

Will use A's move constructor if that move constructor is noexcept, else it will use A's copy constructor. If it uses A's copy constructor, the location of the ints will change. If it uses A's move constructor, the location of the ints will remain stable.

libc++ 和 libstdc++ 将 vector 的移动构造函数标记为 noexcept.因此给 A 一个 noexcept 移动构造函数.

libc++ and libstdc++ mark vector's move constructor as noexcept. And thus give A a noexcept move constructor.

VS-2015 说 A 没有 noexcept 移动构造函数:

VS-2015 says that A does not have a noexcept move constructor:

static_assert(std::is_nothrow_move_constructible<A>::value, "");

不编译.

尽管如此,VS-2015 不会将整数重新分配到新地址,因此看起来它不符合 C++11 规范.

Nevertheless, VS-2015 does not reallocate the ints to a new address, and thus it looks like it is not conforming to the C++11 spec.

如果改变了 libc++ 头文件,使得 vector 移动构造函数没有被标记为 noexcept,那么 int 确实会重新分配.

If one changes the libc++ headers such that the vector move constructor is not marked noexcept, then the ints do indeed reallocate.

委员会最近的讨论表明,每个人都赞成将 vector 的移动构造函数标记为 noexcept(也许 basic_string 也是如此,但不是其他容器).因此,未来的标准可能会保证您所寻求的稳定性.同时,如果:

Recent discussions on the committee indicate that everyone is in favor of marking the move constructor of vector noexcept (and maybe basic_string too, but not other containers). So it is possible that a future standard will guarantee the stability you seek. In the meantime, if:

static_assert(std::is_nothrow_move_constructible<A>::value, "");

编译,然后你有你的保证,否则你没有.

compiles, then you have your guarantee, else you don't.

更新

更新中x2 != y2的原因是这些是vector>中vector的地址;.这些内部元素必须找到一个新的(更大的)缓冲区来生存,就像内部元素是 int 一样.但与 int 不同的是,内部元素 vector 可以通过移动构造函数移动到那里(int 必须复制).但是无论是移动还是复制,内部元素的地址都必须改变(从小的旧缓冲区到大的新缓冲区).此行为与问题的原始部分一致(其中内部元素也显示更改地址).

The reason that x2 != y2 in the update is that these are the addresses of vector<int> in the vector<vector<int>>. These inner elements had to find a new (bigger) buffer to live in, just the same as if the inner element was int. But unlike int, the inner element vector<int> could move there with a move constructor (int had to copy). But whether moving or copying, the address of the inner element had to change (from the small old buffer to the big new buffer). This behavior is consistent with the original part of the question (where the inner element is also shown to change addresses).

是的,LWG 2321 参与其中,但不是有争议的观点.在我的回答中,我已经假设 LWG 2321 已经通过.除了过于急切的调试迭代器无缘无故地(和错误地)使自己无效之外,真的没有其他方法可以让事情发生.非调试迭代器永远不会失效,指针或引用也不会失效.

And yes, LWG 2321 is involved, though not a contentious point. In my answer I've already assumed LWG 2321 has passed. There's really no other way for things to happen aside from overly eager debugging iterators to gratuitously (and incorrectly) invalidate themselves. Non-debugging iterators would never invalidate, and neither will pointers or references.

希望我能够轻松创建带有箭头到缓冲区的动画.那会很清楚.我只是不知道如何在我可用的时间内轻松做到这一点.

Wish I had the ability to easily create an animation with arrows to buffers. That would be really clear. I just don't know how to easily do that in the time I have available.

这篇关于重新分配 std::vector 对象时指向内部数据结构的指针的有效性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆