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)は、標準が機能しない許可を与えていないため、機能するために必要です。
はい、それは安全です、そして、標準ライブラリ実装はそれをするために輪を飛び越えます。
実装者はこの要件をなんとか23.2/11にまでさかのぼると思いますが、どうすればよいかわかりませんし、より具体的なものも見つかりません。私が見つけることができる最高のこの記事です:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
Libc ++およびlibstdc ++の実装を調べると、これらも安全であることがわかります。
この標準は、最初の例でも安全であることを保証します。 C++ 11の引用
[sequence.reqmts]
3表100および101では、
X
はシーケンスコンテナクラスを示し、a
はX
型の要素を含むT
の値を示します。 。t
は、_X::value_type
_の左辺値またはconst右辺値を示します。16表101 ...
式
a.Push_back(t)
戻り値のタイプvoid
操作上のセマンティクス _t.
_Requires:T
はCopyInsertable
intoX
になります。 コンテナ _basic_string
_、deque
、list
、vector
したがって、実装は厳密ではありませんが、_Push_back
_を実行するときに参照が無効にならないことを保証する必要があります。
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 ++ に対して安全です。
libc ++のstd::vector::Push_back
は、メモリの再割り当てが必要なときに__Push_back_slow_path
を呼び出します。
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.
}
最初のバージョンは間違いなく安全ではありません:
標準ライブラリコンテナまたは文字列メンバー関数を呼び出して取得したイテレータの操作は、基になるコンテナにアクセスできますが、変更はできません。 [注:特に、イテレーターを無効にするコンテナー操作は、そのコンテナーに関連付けられているイテレーターに対する操作と競合します。 —終了注]
セクション17.6.5.9から
これはデータ競合に関するセクションであり、通常はスレッド化と関連して考えられますが、実際の定義には「前に起こる」関係が含まれ、Push_back
の複数の副作用間の順序関係はありません。つまり、参照の無効化は、新しいテール要素のコピー構築に関して順序付けられているとは定義されていないようです。
完全に安全です。
2番目の例では
v.reserve(v.size() + 1);
vectorがそのサイズを超えた場合、reserve
を意味するため、これは必要ありません。
あなたのことではなく、ベクターがこのことを担当しています。