2016-05-05 13 views
5

std::vectoroperator[]に新しい要素を挿入する代わりに参照を返す理由はありますか? vector::operatorためcppreference.comページはstd::map::operator[]とは異なりherevector :: operator []がmap :: operator []と同様の方法で実装されていないのはなぜですか?

を言い、このオペレータは、容器の中に新しい要素を挿入することはありません。

map::operator[]says

用ページ「は、そのようなキーが存在しない場合は、挿入を行う、キーにキー同等にマッピングされた値への参照を返します。」間

はなぜvector::operator[]map::operator[]insert(std::make_pair(key, T())).first->second;を呼び出す方法のようにvector::push_backvector::insertを呼び出すことで実現することができませんでしたか?

+3

実際に 'std :: map :: operator []'が実際に実装される方法は、既に十分な混乱を招いています。 UBは境界外にアクセスされる 'std :: vector'にとっては問題ありません。 –

+0

'some_vector [1000]'と 'some_vector'のサイズが10の間に何が起こるか想像してみましょう。まあ、私はちょうど真ん中に990のエントリがあると想像するのか分かりません。 –

+0

@NickyC:しかし、地図にも同じ問題があります! –

答えて

7

非常に単純です:意味がないので。何を期待しますか

std::vector<int> a = {1, 2, 3}; 
a[10] = 4; 

インデックス10を指定したにもかかわらず4番目の要素を作成しますか?要素3〜10を作成し、最後の要素への参照を返しますか?どちらも特に直感的ではありません。

あなたが本当にoperator[]代わりのpush_backを使用して値をベクトルを埋めるためにしたい場合は、それらの設定の前に要素を作成するために、ベクター上resizeを呼び出すことができます。

編集:または、実際にアソシエーティブコンテナを使用する場合は、注文とは別にインデックスが重要な場合は、実際にはstd::map<int, YourData>が理にかなっている可能性があります。

+2

重要な点を強調する価値があるかもしれません: 'map'では、演算子は多くても指定されたキーの要素を1つだけ挿入します。 'vector'の場合、他のインデックスに*その他の*要素を挿入する必要があります。 –

+0

+1。また、C++は多くの言語(Javaなど)よりもパフォーマンスの問題を考慮しています。これがvector :: operator [] 'が境界チェックをしない理由です。あなたがそれらを必要と感じたら手動でオプトインすることができますが、C++はオーバーヘッドを必要としません。たとえベクトル中に存在しない要素を挿入することが意味をなさないとしても、それはパフォーマンス目標をかなり直接矛盾させるでしょう。境界チェックの要望と同様に、要素を挿入したい場合は、自分自身でベクトル化してオプトインすることができます。 – GManNickG

+0

@ GManNickG基本的に、存在しない要素を挿入したいのであれば、ベクトルがシーケンスコンテナなので地図を使わなければなりませんか? – 1337ninja

1

マップとベクターは全く異なる概念です。マップは「結合コンテナ」であり、ベクトルは「シーケンスコンテナ」です。相違を描写することはこの答えの範囲外ですが、地図は一般的に赤黒の木として実装されていますが、ベクトルはCスタイルの配列を畳み込んだラッパーです(連続して格納される要素メモリ)。

要素がすでに存在するかどうかを確認する場合は、コンテナ全体のサイズを変更する必要があります。要素を削除するとどうなりますか?作成したエントリではどうしますか?マップ付き:

std::map<int, int> m; 
m[1] = 1; 
m.erase(m.begin()); 

これは一定の操作です。ベクターで

std::vector<int> v; 
// ... initialize some values between 25 and 100 
v[100] = 1; 
v.erase(v.begin() + 25, v.end()); 

これは線形動作です。それは地図に対してひどく(比較的)非効率的です。これは実例ですが、これが他のシナリオでどのように爆発するかを想像するのは難しくありません。最低でも、ほとんどの人は、それ自体のコスト(保守とコードの複雑さ)であるoperator[]を避けるために道を離れます。

+0

+1ベクトルとポインタが2つの別々のデータ構造に基づいているという事実に触れたからです。ベクトルはかなり多くの空想的な配列ですが、マップはポインタに基づいています。イテレータを使用して検索する必要がある場合は、マップが最適です。あなたが配列の中で必要な要素を知っているなら、Vectorsは素晴らしいです。 – Caperneoignis

0

std :: vectorの演算子[]は、新しい要素を挿入する代わりに参照を返すだけの理由はありますか?

std::vector::operator[]は、std::vectorが配列コンテナ(つまり、配列のようなもの)であるため、配列のように実装されます。整数型の標準配列には範囲外でアクセスすることはできません。同様に、ベクトルの長さ以外のインデックスを持つstd::vector::operator[]にアクセスすることもできません。だから、あなたが尋ねるように実装されていない理由は、それ以外の文脈ではC++の配列はそういうふうに動作するからです。

std::map::operator[]はシーケンスコンテナではありません。その構文は、他の言語の連想配列に似ています。 C++(そしてその前身、C)の面では、map::operator[]は単なる構文的な砂糖です。 operator[]ファミリーの「黒い羊」で、std::vector::operator[]ではありません。

C++仕様の興味深い部分は、存在しないキーを持つマップにアクセスすることです。std::map::operator[]を使用すると、がマップに追加されます。

m['a'] == 1, m.size() == 1 
m['b'] == 0, m.size() == 2 

も参照:Difference between map[] and map.at in C++?

要素場合鍵は、findaMap.end()戻る存在しない場合[map::at]例外をスローによって、

#include <iostream> 
#include <map> 
int main(void) { 
    std::map<char, int> m; 
    m['a'] = 1; 
    std::cout << "m['a'] == " << m['a'] << ", m.size() == " << m.size() << std::endl; 
    std::cout << "m['b'] == " << m['b'] << ", m.size() == " << m.size() << std::endl; 
} 

結果存在しません。operator[]値の初期化対応するキーの新しい値がない場合そこには別名が存在する。

関連する問題