假设有一个包含int vector 的A类。现在假设创建了一个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。
最佳答案
不幸的是,这不能保证。话虽如此,当前所有三个实现(libc++,libstdc++和VS-2015)似乎都可以保证这一点。问题是A
的move构造函数是否为noexcept:
static_assert(std::is_nothrow_move_constructible<A>::value, "");
A
的move构造函数由编译器提供,因此取决于std::vector<int>
的move构造函数。如果std::vector<int>
的move构造函数为noexcept,则A
的move构造函数为noexcept,否则不是。当前的草案N4296并未将
vector
的move构造函数标记为noexcept。但是,它允许实现。这行:
aVec.resize(30);
如果move构造函数不是noexcept,则将使用
A
的move构造函数,否则将使用A
的copy构造函数。如果使用A
的副本构造函数,则int的位置将更改。如果使用A
的move构造函数,则int的位置将保持稳定。libc++和libstdc++将
vector
的move构造函数标记为noexcept。因此,给A
一个noexcept move构造函数。VS-2015表示
A
没有noexcept move构造函数:static_assert(std::is_nothrow_move_constructible<A>::value, "");
不编译。
但是,VS-2015不会将整数重新分配给新地址,因此看起来它不符合C++ 11规范。
如果更改了libc++头文件,使得
vector
move构造函数未标记为noexcept,则int确实会重新分配。委员会最近的讨论表明,每个人都赞成标记
vector
noexcept(也可能是basic_string
,但不是其他容器)的move构造函数。因此,将来的标准可能会保证您寻求的稳定性。同时,如果:static_assert(std::is_nothrow_move_constructible<A>::value, "");
编译,那么您就有了保证,否则,您没有保证。
更新
更新中
x2 != y2
的原因是这些是vector<int>
中vector<vector<int>>
的地址。这些内部元素必须找到一个新的(更大)缓冲区才能使用,就像内部元素是int
一样。但是与int
不同,内部元素vector<int>
可以使用move构造函数移动到那里(必须复制int
)。但是,无论是移动还是复制,内部元素的地址都必须更改(从较小的旧缓冲区更改为较大的新缓冲区)。此行为与问题的原始部分一致(其中内部元素也显示为更改地址)。是的,虽然没有争议,但涉及到LWG 2321。在我的回答中,我已经假定LWG 2321已通过。除了过度渴望调试迭代器无偿(无效地)使自身无效之外,实际上没有其他事情可以发生。无调试的迭代器永远不会使指针无效,引用或引用也不会无效。
希望我能够轻松创建带有箭头的动画到缓冲区。那真的很清楚。我只是不知道在我有空的时候如何轻松地做到这一点。
关于c++ - 重新分配std::vector对象时指向内部数据结构的指针的有效性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27735754/