假设有一个包含int向量的A类.现在假设创建了A的向量.如果由于push_back而发生A对象的重新分配(因此移动了矢量对象),那么指向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";
}
Run Code Online (Sandbox Code Playgroud)
运行此代码可以得到:
0x1810088 - 0x1810028 - 2
0x1810088 - 0x18100c8 - 30
Run Code Online (Sandbox Code Playgroud)
所以它表明指针仍然有效.但我想确保这是有保证的,而不仅仅是机会.我倾向于说这是有保证的,因为向量的内部数据是动态分配的,但是,再次,只是想检查.
我看过这里[ 迭代器失效规则 ],但它没有考虑这种特殊情况(即矢量对象本身的重新分配).
更新:
我试过这个来检查我在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";
Run Code Online (Sandbox Code Playgroud)
得到了这个:
0x16f0098 - 0x16f0048 - 2
0x16f0098 - 0x16f00c8 - 30
Run Code Online (Sandbox Code Playgroud)
这对我来说很奇怪.我期望x2 == y2.
How*_*ant 11
不幸的是,这不能保证.话虽如此,所有3个当前实现(libc ++,libstdc ++和VS-2015)似乎都能保证它.问题是移动构造函数是否A为noexcept:
static_assert(std::is_nothrow_move_constructible<A>::value, "");
Run Code Online (Sandbox Code Playgroud)
移动构造函数A是由编译器提供的,因此依赖于移动构造函数std::vector<int>.如果移动构造函数std::vector<int>是noexcept,那么移动构造函数A是noexcept,否则它不是.
当前草案N4296并未将移动构造函数标记vector为noexcept.但是它允许实现这样做.
这一行:
aVec.resize(30);
Run Code Online (Sandbox Code Playgroud)
将使用A的移动构造函数如果移动构造函数是noexcept,否则将使用A的拷贝构造函数.如果它使用A的是复制构造函数,则int的位置将会改变.如果它使用A移动构造函数,则int的位置将保持稳定.
libc ++和libstdc ++标记vector的移动构造函数为noexcept.从而给出A一个noexcept移动构造函数.
VS-2015说A没有noexcept移动构造函数:
static_assert(std::is_nothrow_move_constructible<A>::value, "");
Run Code Online (Sandbox Code Playgroud)
不编译.
尽管如此,VS-2015并没有将int重新分配给新地址,因此看起来它不符合C++ 11规范.
如果更改了libc ++头文件,使得vector移动构造函数未标记为noexcept,则int确实会重新分配.
最近关于委员会的讨论表明,每个人都赞成标记vectornoexcept 的移动构造函数(可能basic_string也是,但不是其他容器).因此,未来的标准有可能保证您寻求的稳定性.与此同时,如果:
static_assert(std::is_nothrow_move_constructible<A>::value, "");
Run Code Online (Sandbox Code Playgroud)
编译,然后你有你的保证,否则你没有.
更新
究其原因是x2 != y2在更新的是,这些的地址vector<int>中vector<vector<int>>.这些内部元素必须找到一个新的(更大的)缓冲区才能生存,就像内部元素一样int.但与之不同的是int,内部元素vector<int>可以使用移动构造函数移动(int必须复制).但无论是移动还是复制,内部元素的地址都必须改变(从小的旧缓冲区到大的新缓冲区).此行为与问题的原始部分一致(内部元素也显示为更改地址).
是的,LWG 2321参与其中,虽然不是一个有争议的问题.在我的回答中,我已经假设LWG 2321已经过去了.除了过于急切地调试迭代器以无偿地(并且错误地)使自己无效之外,真的没有别的办法可以实现.非调试迭代器永远不会失效,指针或引用也不会失效.
希望我有能力轻松创建带有箭头缓冲区的动画.那将是非常清楚的.我只是不知道如何在我有空的时候轻松做到这一点.
| 归档时间: |
|
| 查看次数: |
831 次 |
| 最近记录: |