std :: vector要素は連続していることが保証されていますか?


111

私の質問は単純です:std :: vector要素が連続していることが保証されていますか?つまり、std :: vectorの最初の要素へのポインタをC配列として使用できますか?

私の記憶がうまく機能するなら、C ++標準はそのような保証をしませんでした。ただし、std :: vectorの要件は、要素が隣接していない場合、要件を満たすことが実質的に不可能であるようなものでした。

誰かがこれを明確にできますか?

例:

std::vector<int> values;
// ... fill up values

if( !values.empty() )
{
    int *array = &values[0];
    for( int i = 0; i < values.size(); ++i )
    {
        int v = array[i];
        // do something with 'v'
    }
}

valuesそのifブロック内で変異すると、問題が発生することを知っています。質問の答えはわかりませんので、コメントを残しておきます。:)
グレッグ・D

@グレッグ:どのような問題–もう少し詳しく説明できますか?
Reunanen、2009年

新しい値をプッシュすると「realloc」がトリガーされ、配列が無効になる可能性があると彼は言っていました。
Martin Cote

mutate values、具体的にはそのサイズを変更する(たとえば、push_back())呼び出しは、にコピーされたポインターを無効にする基になるベクトルの再割り当てを要求する場合がありますarray。これは、ベクターへのポインターの代わりにvector :: iteratorを使用する場合と同じ原理です。:)
グレッグ・D

1
ええ、私は ``の周りの値を入れて、クラスに含まれている値ではなく、クラス自体について話していることを明確にするようにしています。:)不運なネーミングなど。私はそれがこの質問が関連している一般的なケースでは本当に問題だとは思いません-なぜ誰かがメモリへのポインタをつかんで、ポインタを使用する代わりにベクトルでいじくり始めるのでしょうか?愚かさ。
Greg D

回答:


118

これはC ++ 98標準本体にはありませんでしたが、後でTRの一部として追加されました。今後のC ++ 0x標準には、もちろんこれが要件として含まれます。

n2798から(C ++ 0xのドラフト):

23.2.6クラステンプレートベクトル[ベクター]

1ベクターは、ランダムアクセス反復子をサポートするシーケンスコンテナーです。さらに、それは最後に(償却された)一定時間の挿入および消去操作をサポートします。途中で挿入と消去を行うと、線形時間がかかります。ストレージ管理は自動的に処理されますが、効率を向上させるためのヒントを与えることができます。ベクトルの要素は連続して格納されます。つまり、vがTがbool以外の型であるベクトルの場合、すべての0 <= n <vについて、同一性&v [n] ==&v [0] + nに従います。 。サイズ()。


3
これは、ISO 14882、第2版でも説明されています。セクション23.2.4 [lib.vector]:「ベクトルの要素は連続して格納されます。つまり、vがvector <Tの場合、Allocator>で、Tは、 boolの場合、すべての0 <= n <v.size()について、同一性&v [n] ==&v [0] + nに従います。
Mike Caron

4
so s、TR、TC、:)実際には、C ++ 03はC ++ 98-TC1(技術的修正)とも呼ばれています。これは、私が読んだもの
Johannes Schaub-litb

2
ベクターのベクターはどうですか?İ内側のベクトルは最後のグループの内側のベクトルの直後ですか?
フセイントゥグルルブキシク2016

1
@huseyin tugrul buyukisikこれに対する答えを学びましたか?私はまた、これがどのように機能するのか疑問に思っています
David Doria

1
@huseyin tugrul buyukisikもちろん本当ですが、std::vector連続しているのはそれ以降のインスタンスです。例えば:でstd::vector<std::vector<int>> v要素v[0]v[1]...メモリに保存され、その後、しかし要素されているv[0].back()v[1].front()ことが保証されていません。
jarzec 2017年

27

他の答えが指摘したように、ベクトルの内容は連続であることが保証されています(ブールの奇妙さを除く)。

私が追加したかったコメントは、ベクターにメモリを再割り当てさせる可能性のあるベクターの挿入または削除を行うと、保存されているすべてのポインターとイテレーターが無効になるということです。


1
要素は連続したメモリブロックに保存されますが、別の場所にあるだけです。問題は特に隣接性についてでした。
ディマ

2
しかし、既存のポインターとイテレーターは無効になります。
ビル・リンチ

いい視点ね。あなたはそれをあなたの答えに入れて、あなたの意味を明確にすべきです。
ディマ

私にとって最も
役立つ

これで、昨日、特定の要素を削除するダブルループでループしたときに、なぜ私のプログラムがsegfaultしていたのかわかります:)ありがとう!
user2891462 2015

9

この標準は、実際にはa vectorがメモリ内で連続的であり、配列を&a[0]必要とするC関数に渡すことができることを保証しています。

このルールの例外vector<bool>は、1ビットあたり1ビットしか使用boolしないことです。これには連続メモリはありますが、aとしては使用できませんbool*(これは、誤った最適化とミスであると広く見なされています)。

ところで、イテレータを使ってみませんか?それが彼らの目的です。


1
>ところで、イテレータを使ってみませんか?それが彼らの目的です。たぶん彼は、トピックにAlexanrescuの新しい論文を読む:boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/...
ネマニャTrifunovic

リンクをありがとう、私はそれを私の読書リストに入れます(私はAlexandresuの記事を見逃さないようにします)
Motti

ムワハハ、最近みんながそのプレゼンテーションについて話しているようです。見て、それについての議論はまだ熱いです:groups.google.com/group/comp.lang.c++
Johannes Schaub-litb 2009年

注意深く読むと、Alexandrescuの記事には「C ++ではイテレーターを使用しないでください」とは書かれておらず、「Dをチェックアウト」と書かれています。この論文で彼が説明するアプローチは、機能的遺産(List、Scheme、Haskell)を吸収している既存の言語やフレームワークに驚くほど似ており、まだ別のCベースの構文がより良い理想的な出発点であるかどうかを真剣に疑っていますリスト処理。昨年、私は彼にかなりの才能をC#のような既に確立された言語の改善に向けさせるように少しの間説得しようとしましたが、成功しませんでした!:)
ダニエル・イアウィッカー

6

他の人がすでに言ったように、vector内部ではオブジェクトの連続した配列を使用します。そのconstメンバー関数がIIRCと呼ばれるときはいつでも、その配列へのポインターは無効として扱われるべきです。

ただし、例外があります!!

vector<bool>スペースを節約するように設計された特別な実装があるため、各ブール値は1ビットのみを使用します。基になる配列はブールの連続した配列ではなく、配列演算は上とvector<bool>同じように機能しませんvector<T>

(私は常に新しいものを実装できるので、これがベクトルのどの特殊化にも当てはまる可能性があると思います。しかし、std::vector<bool>単純なポインタ演算が機能しない唯一の、err、標準の特殊化です。)


ユーザーはspecializeを許可されておらずstd::vector、他のすべてのベクターは連続したストレージを使用する必要があります。したがって、std::vector<bool>は(幸いなことに)奇妙な唯一の標準ベクトルです。(私はこの専門化が廃止され、たとえばstd::dynamic_bitsetほぼ同じ機能を持つに置き換えられるべきだという意見に強く同意します。これは悪いデータ構造ではなく、単なるベクトルではありません。)
Arne Vogel

3

このスレッドを見つけたのは、連続したメモリを使用するベクトルが有利であるユースケースがあるためです。

OpenGLで頂点バッファーオブジェクトを使用する方法を学習しています。バッファロジックを含むラッパークラスを作成したので、フロートの配列といくつかの構成値を渡してバッファを作成するだけです。ユーザー入力に基づいて関数からバッファーを生成できるようにしたいので、コンパイル時に長さがわかりません。このようなことをするのが最も簡単な解決策です:

void generate(std::vector<float> v)
{
  float f = generate_next_float();
  v.push_back(f);
}

これで、ベクターの浮動小数点数を配列としてOpenGLのバッファー関連関数に渡すことができます。これにより、配列の長さを決定するためのsizeofの必要もなくなります。

これは、フロートを格納するために巨大な配列を割り当てて、十分な大きさにしたり、連続したストレージで独自の動的配列を作成したりするよりもはるかに優れています。


2
この機能は私には意味がありません。それ自体vではなく参照またはポインタを渡すことを意味しますvか?v単独で渡すと関数内にコピーが作成され、関数が終了するとコピーは存在しなくなるためです。したがって、関数が終了したときにベクターを削除するためだけにベクターに何かをプッシュしています。
johnbakers 2013

1

cplusplus.com:

ベクトルコンテナーは動的配列として実装されます。通常の配列と同様に、ベクターコンテナーの要素は連続した格納場所に格納されます。つまり、イテレーターを使用するだけでなく、要素への通常のポインターのオフセットを使用しても要素にアクセスできます。


1

はい、std :: vectorの要素は連続していることが保証されています。


正しい。私は私があまりにも多くのものを:)の使用を推測
ブノワ・
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.