11

vector既存の要素のコピーを挿入して 2 倍にしようとしています。次のコードは以前のバージョンでは機能しましたが、Visual Studio 2010 では失敗します。

#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
{
   vector<int> test;
   test.push_back(1);
   test.push_back(2);
   test.insert(test.begin(), test[0]);
   cout << test[0] << " " << test[1] << " " << test[2] << endl;
   return 0;
}

出力は-17891602 1 2です1 1 2

なぜそれが起こっているのかを理解しました-ベクトルが再割り当てされており、挿入ポイントにコピーされる前に参照が無効になります。古い Visual Studio は明らかに異なる順序で処理を行っていたため、未定義の動作の結果の 1 つが正しく機能することであることが証明され、また、それが決して信頼できるものではないことも証明されました。

この問題を解決するために、2 つの異なる方法を考え出しました。reserve1 つは、再割り当てが行われないようにするために使用する方法です。

   test.reserve(test.size() + 1);
   test.insert(test.begin(), test[0]);

もう 1 つは、参照が有効なままであることに依存しないように、参照からコピーを作成することです。

template<typename T>
T make_copy(const T & original)
{
    return original;
}

   test.insert(test.begin(), make_copy(test[0]));

どちらも機能しますが、どちらも自然な解決策とは思えません。足りないものはありますか?

4

2 に答える 2

4

問題はvector::insert、値ではなく、2 番目のパラメーターとして値への参照を取ることです。コピーを作成するためにテンプレートは必要ありません。コピー コンストラクターを使用して、参照渡しされる別のオブジェクトを作成するだけです。ベクトルのサイズが変更されても、このコピーは有効なままです。

#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
{
   vector<int> test;
   test.push_back(1);
   test.push_back(2);
   test.insert(test.begin(), int(test[0]));
   cout << test[0] << " " << test[1] << " " << test[2] << endl;
   return 0;
}
于 2012-04-20T04:08:16.083 に答える
1

これは定義された動作だと思います。2011 §23.2.3C++ 標準では、表 100 にシーケンス コンテナーの要件が一覧表示されており、この場合のエントリがあります。式の例を示します

a.insert(p,t)

ここで、aは の値でXあり、 は 型の要素を含むシーケンス コンテナー型でありTpは への定数反復子aであり、は 型のt左辺値または const 右辺値です。X::value_typeT

この式のアサーションは次のとおりです。

必須: TCopyInsertable入る必要がありますXvectorとのためにdequeTも となりますCopyAssignable
効果:t beforeのコピーを挿入しますp

私が見つけた唯一の関連するベクトル固有の引用は、§23.3.6.5段落1にあります。

備考:新しいサイズが古い容量より大きい場合、再割り当てが発生します。再割り当てが発生しない場合、挿入ポイントの前のすべての反復子と参照は有効なままです。

insertこれはベクトルが再割り当てされることを示していますが、シーケンス コンテナーの以前の要件を例外にするものではありません。

この問題の回避策として、要素のコピーを作成してそのコピーを挿入するという @EdChum の提案に同意します。

于 2012-04-19T00:30:33.000 に答える