假设有一个类 A 包含一个整数 vector 。现在假设创建了一个 A 的 vector 。例如,如果由于 push_back 而发生 A 对象的重新分配(因此 vector 对象被移动),指向 int 本身的指针是否仍然有效?这有保证吗?
澄清一下:
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
所以它表明指针仍然有效。但我想确保这是有保证的,而不仅仅是偶然的事情。我倾向于说这是有保证的,因为 vector 的内部数据是动态分配的,但再次,只是想检查一下。
我看过这里 [ Iterator invalidation rules ] 但它没有考虑这种特定情况(即重新分配 vector 对象本身)。
更新:
我试过这个来检查我在评论中为 Jarod42 的回答写了什么:
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。
最佳答案
很遗憾,这无法保证。话虽如此,所有 3 个当前实现(libc++、libstdc++ 和 VS-2015)似乎都可以保证这一点。问题是 A
的移动构造函数是否是noexcept:
static_assert(std::is_nothrow_move_constructible<A>::value, "");
A
的移动构造函数由编译器提供,因此依赖于 std::vector<int>
的移动构造函数.如果 std::vector<int>
的移动构造函数是 noexcept,那么 A
的移动构造函数是 noexcept,否则不是。
当前草案 N4296 未标记 vector
的移动构造函数作为无异常(exception)。但是它允许实现这样做。
这一行:
aVec.resize(30);
将使用 A
的移动构造函数,如果移动构造函数是 noexcept,否则它将使用 A
的复制构造函数。如果它使用 A
的复制构造函数,整数的位置会改变。如果它使用 A
的移动构造函数,整数的位置将保持稳定。
libc++ 和 libstdc++ 标记 vector
的移动构造函数为 noexcept。因此给A
一个 noexcept 移动构造函数。
VS-2015 说 A
没有 noexcept 移动构造函数:
static_assert(std::is_nothrow_move_constructible<A>::value, "");
不编译。
尽管如此,VS-2015 不会将整数重新分配到新地址,因此它看起来不符合 C++11 规范。
如果更改 libc++ 头文件,使得 vector
move构造函数没有标记为noexcept,那么int确实会重新分配。
委员会最近的讨论表明,每个人都赞成标记 vector
的移动构造函数。 noexcept(也可能是 basic_string
,但不是其他容器)。因此, future 的标准可能会保证您寻求的稳定性。同时,如果:
static_assert(std::is_nothrow_move_constructible<A>::value, "");
编译,然后你有你的保证,否则你没有。
更新
x2 != y2
的原因在更新中,这些是 vector<int>
的地址在 vector<vector<int>>
.这些内部元素必须找到一个新的(更大的)缓冲区来生存,就像内部元素是 int
一样。 .但不像 int
, 内部元素 vector<int>
可以使用移动构造函数移动到那里(int
必须复制)。但是无论是移动还是复制,内部元素的地址都必须改变(从小的旧缓冲区到新的大缓冲区)。此行为与问题的原始部分一致(其中内部元素也显示为更改地址)。
是的,LWG 2321涉及,虽然不是一个有争议的点。在我的回答中,我已经假设 LWG 2321已通过。除了过度急切地调试迭代器以无偿(并且错误地)使自己无效之外,真的没有其他方法可以让事情发生。非调试迭代器永远不会失效,指针或引用也不会。
希望我能够轻松创建带有指向缓冲区的箭头的动画。那会很清楚。我只是不知道如何在我有空的时候轻松做到这一点。
关于c++ - 重新分配 std::vector 对象时指向内部数据结构的指针的有效性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27735754/