3

次のコードで、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。

operator[] または front()/back() メソッドから取得した参照を insert() に渡すことは有効ですか?

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
}

したがって、今のところ、次の 2 つのオプションしかありません。

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 で期待される結果とパフォーマンスが得られます。

私は何か見落としてますか?より良い解決策はありますか?

4

2 に答える 2

4

insert は参照によって 2 番目の引数を取ります。op[] も参照を提供します。insert を呼び出すと参照が無効になるため、未定義の動作が発生し、何でも発生する可能性があります。

于 2013-06-06T15:35:47.460 に答える
3

挿入のためのドキュメントは言う(私の強調)

ベクターは、指定された位置の要素の前に新しい要素を挿入することによって拡張され、挿入された要素の数だけコンテナーのサイズが効果的に増加します。

これにより、新しいベクトル サイズが現在のベクトル容量を超えた場合にのみ、割り当てられたストレージ スペースが自動的に再割り当てされます。

の 2 番目の引数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);
于 2013-06-06T15:35:08.313 に答える