vector::insert在VS2010中执行意想不到的结果

vector::insert in VS2010 performs unexpected result

本文关键字:意想不到 结果 执行 VS2010 insert vector      更新时间:2023-10-16

我偶尔在VS2010中发现一个奇怪的问题,下一个代码:

void Test1()
{
    std::vector<int> vec;
    vec.push_back(10);
    vec.push_back(20);
    vec.insert(vec.end(), vec[0]);
    // GCC: vec == [10, 20, 10];
    // VS2005: vec == [10, 20, 10];
    // VS2010: vec == [10, 20, -17891602];
}

似乎vector在读取新值之前重新分配内存并删除旧内存,这导致了损坏值的复制。这个问题在VS2010中存在。在VS2005和GCC中检查- OK

传递给insert()从操作符[]或front()/back()方法中获取的引用是否有效?

UPD:根据下面的评论做了一些调查后,我得出结论,由于性能的原因,使用reserve()不是一个好主意。这会导致不必要的大量重新分配。

void Test2()
{
    std::vector<int> vec, vec2;
    const int count = 10000;
    int prevCap = 0, reallocCount = 0;
    int prevCap2 = 0, reallocCount2 = 0;
    for (int i = 0; i < count; ++i)
    {
        if (vec.size() >= vec.capacity())
        {
            vec.reserve(vec.size()+1);
        }
        vec.insert(vec.end(), i);
        vec2.insert(vec2.end(), i);
        const int cap = vec.capacity();
        const int cap2 = vec2.capacity();
        if (prevCap != cap) ++reallocCount;
        prevCap = cap;
        if (prevCap2 != cap2) ++reallocCount2;
        prevCap2 = cap2;
    }
    cout << reallocCount << " " << reallocCount2 << endl;
    // reallocCount == 10000, reallocCount2 == 15 GCC
}

所以现在我只有两个选项:

1)使用临时变量
const int tempValue = vec[0];
vec.insert(vec.end(), tempValue);

但是我不确定tempValue是否可以通过一些优化被编译器删除。

2)使用push_back(0)和进一步的pop_back()调用

vec.push_back(0);
vec.pop_back();
vec.insert(vec.end(), vec[0]);

这种方法似乎更好,它在VS2005/2010和GCC中给出了预期的结果和性能。

我错过什么了吗?有没有更好的解决方案?

insert通过引用接受第二个参数。Op[]也提供了参考。调用insert会使引用无效,因此会有未定义的行为,任何事情都可能发生。

插入的文档说(我强调)

通过在元素at之前插入新元素来扩展vector指定的位置,有效地增加了容器的尺寸插入的元素数。

这将导致自动重新分配已分配的存储空间当且仅当新向量的大小大于当前向量能力。

由于insert的第二个参数是一个引用,重新分配可能需要重新定位堆单元,这表明只有当您知道重新分配不会发生时,您的代码才是安全的。即当vec.capacity() > vec.size()

正如Agnew和quetzalcoatl所指出的,有几种方法可以修复你的代码

您可以将新值复制到临时值,以确保对它的引用仍然有效

int val = vec[0];
vec.insert(vec.end(), val);

…或者你可以在调用insert

之前增加向量的容量
if (vec.capacity == vec.size()) {
    vec.reserve(vec.size()+1);
}
vec.insert(vec.end(), val);