vector<int> v;
v.push_back(1);
v.push_back(v[0]);
2番目のpush_backによって再割り当てが発生した場合、ベクトルの最初の整数への参照は無効になります。これは安全ではありませんか?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
これは安全ですか?
vector<int> v;
v.push_back(1);
v.push_back(v[0]);
2番目のpush_backによって再割り当てが発生した場合、ベクトルの最初の整数への参照は無効になります。これは安全ではありませんか?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
これは安全ですか?
回答:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526は、標準の潜在的な欠陥としてこの問題(またはそれに非常に類似したもの)に対処したようです。
1)const参照によって取得されるパラメーターは、関数の実行中に変更できます
例:
与えられたstd :: vector v:
v.insert(v.begin()、v [2]);
v [2]は、ベクトルの要素を移動することで変更できます
提案された解決策は、これは欠陥ではないということでした:
vector :: insert(iter、value)は、標準が機能しないことを許可していないため、機能する必要があります。
v.insert(v.begin(), v[2]);
再割り当てをトリガーすることはできません。それで、これはどのように質問に答えますか?
はい、それは安全であり、標準ライブラリの実装はそれを行うためにフープをジャンプします。
私は実装者がこの要件を23.2 / 11に遡ると信じていますが、私はその方法を理解できず、より具体的なものも見つけられません。私が見つけることができる最高のものはこの記事です:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
libc ++とlibstdc ++の実装を検査すると、それらも安全であることがわかります。
vec.insert(vec.end(), vec.begin(), vec.end());
ますか?
vector.push_back
それ以外の場合は指定します。「新しいサイズが古い容量より大きい場合、再割り当てが発生します。」そして(at reserve
)「再割り当ては、シーケンスの要素を参照するすべての参照、ポインタ、イテレータを無効にします。」
この規格は、最初の例でも安全であることを保証しています。C ++ 11の引用
[sequence.reqmts]
3表100および101で、...
X
はシーケンスコンテナークラスをa
示し、をX
含む型の要素の値を示しT
ます...t
は、左辺値または定数右辺値を示しますX::value_type
16表101 ...
式
a.push_back(t)
戻り値の型void
動作セマンティクスt.
必要なT
もののコピーを追加します:にCopyInsertable
入れX
ます。 コンテナbasic_string
、deque
、list
、vector
したがって、それは厳密なことではありませんが、実装では、を実行するときに参照が無効にならないことを保証する必要がありpush_back
ます。
t
になります。唯一の問題は、コピーを作成する前か後かです。あなたの最後の文は確かに間違っています。
t
リストされた前提条件を満たしているため、説明されている動作が保証されます。実装は、前提条件を無効にしてから、それを指定どおりに動作しない言い訳として使用することはできません。
for_each
はイテレータを無効にしないことが要求されると私は信じています。のリファレンスを思い付くことはできませんがfor_each
、「opおよびbinary_opはイテレータまたはサブ範囲を無効にしない」などの一部のアルゴリズムテキストを参照しています。
最初の例が安全であることは明らかではありません。なぜなら、の最も簡単な実装はpush_back
、必要に応じて最初にベクトルを再割り当てしてから、参照をコピーすることです。
しかし、少なくともVisual Studio 2010では安全であると思われます。その実装はpush_back
、ベクター内の要素をプッシュバックした場合に特別な処理を行います。コードは次のように構成されています。
void push_back(const _Ty& _Val)
{ // insert element at end
if (_Inside(_STD addressof(_Val)))
{ // push back an element
...
}
else
{ // push back a non-element
...
}
}
これは標準からの保証ではありませんが、別のデータポイントとしてv.push_back(v[0])
、LLVMのlibc ++にとって安全です。
std::vector::push_back
__push_back_slow_path
メモリを再割り当てする必要がある場合のlibc ++の呼び出し:
void __push_back_slow_path(_Up& __x) {
allocator_type& __a = this->__alloc();
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1),
size(),
__a);
// Note that we construct a copy of __x before deallocating
// the existing storage or moving existing elements.
__alloc_traits::construct(__a,
_VSTD::__to_raw_pointer(__v.__end_),
_VSTD::forward<_Up>(__x));
__v.__end_++;
// Moving existing elements happens here:
__swap_out_circular_buffer(__v);
// When __v goes out of scope, __x will be invalid.
}
__swap_out_circular_buffer
ます。その場合、この実装は確かに安全です。
__swap_out_circular_buffer
。(私はそれを注記するためにいくつかのコメントを追加しました。)
最初のバージョンは間違いなく安全ではありません:
標準ライブラリコンテナーまたは文字列メンバー関数を呼び出して取得したイテレーターの操作は、基になるコンテナーにアクセスできますが、変更することはできません。[注:特に、イテレーターを無効にするコンテナー操作は、そのコンテナーに関連付けられているイテレーターの操作と競合します。—エンドノート]
セクション17.6.5.9から
これは、データレースに関するセクションであり、通常はスレッド化と関連して考えられますが、実際の定義には「前に起こる」関係が含まpush_back
れます。ここで再生します。つまり、参照の無効化は、新しいテール要素のコピー構築に関して順序付けられたものとして定義されていないようです。
v[0]
イテレータではなく、同様にpush_back()
イテレータを取りません。したがって、言語弁護士の観点からは、あなたの主張は無効です。ごめんなさい。ほとんどのイテレータはポインタであり、イテレータを無効にするポイントは参照の場合とほとんど同じですが、引用する標準の部分は、現在の状況とは無関係です。
x.push_back(x[0])
は安全だと言います。
push_backは参照ではなく値をコピーするため、どちらも安全です。ポインターを格納している場合でも、ベクターに関する限りは安全ですが、ベクターの2つの要素が同じデータを指すことを知っているだけです。
セクション23.2.1一般的なコンテナ要件
16
- a.push_back(t)tのコピーを追加します。必要:TはXへのCopyInsertableでなければならない。
- a.push_back(rv)rvのコピーを追加します。必要:TはXへのMoveInsertableです。
したがって、push_backの実装では、のコピー v[0]
が挿入されるようにする必要があります。反例として、コピーする前に再割り当てする実装を想定するv[0]
と、コピーが確実に追加されず、仕様に違反することになります。
push_back
ただし、ベクトルのサイズも変更され、単純な実装では、コピーが行われる前に参照が無効になります。したがって、標準からの引用でこれを裏付けることができない限り、私はそれを間違っていると考えます。
push_back
値をベクトルにコピーします。しかし、(私が見る限り)再割り当て後に発生する可能性があります。その時点で、コピー元の参照は無効になります。
23.3.6.5/1から: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
最後に挿入しているので、ベクターのサイズが変更されない場合、参照は無効になりません。そのため、ベクターの場合は機能するcapacity() > size()
ことが保証され、そうでない場合は未定義の動作であることが保証されます。
references
。引用の部分に興味があります。
push_back
)。
push_back
ました。別の投稿者は、あなたが説明したケースを適切に処理しなかったというバグを指摘しました。他の誰も、私の知る限り、これはバグではないと主張しました。それが決定的な証拠であると言っているのではなく、単なる観察です。