私はこのパーティーに遅れて参加し、現時点で他の回答が完全に正しいとは思わないため、追加の回答を提供します。
質問:
移動元のベクトルは常に空ですか?
答え:
通常、常にではありません。
悲惨な詳細:
vector
一部のタイプのように、標準定義の移動元状態はありません (たとえば、移動元の後にunique_ptr
等しいと指定されています)。nullptr
ただし、要件vector
はそれほど多くのオプションがないようなものです。
vector
答えは、のムーブ コンストラクターとムーブ代入演算子のどちらについて話しているかによって異なります。vector
後者の場合、答えはのアロケータにも依存します。
vector<T, A>::vector(vector&& v)
この操作の複雑さは一定でなければなりません。v
つまり、constructからリソースを盗んで、空の状態の*this
ままにする以外に選択肢がないことを意味します。v
これは、アロケーターA
が何であれ、型T
が何であるかに関係なく当てはまります。
したがって、移動コンストラクターの場合、移動元vector
は常に空になります。これは直接指定されていませんが、複雑さの要件から外れており、それを実装する他の方法がないという事実です。
vector<T, A>&
vector<T, A>::operator=(vector&& v)
これはかなり複雑です。3 つの主要なケースがあります。
1:
allocator_traits<A>::propagate_on_container_move_assignment::value == true
(propagate_on_container_move_assignment
に評価されtrue_type
ます)
この場合、move 代入演算子は 内のすべての要素を破壊*this
し、アロケータ from を使用して容量の割り当てを解除し、アロケータを*this
move 代入してから、メモリ バッファの所有権を から に転送v
し*this
ます。の要素の破棄を除いて*this
、これは O(1) の複雑な操作です。また、通常 (すべてではありませんがほとんどの std:: アルゴリズムで)、move 割り当ての lhs は move 割り当てのempty() == true
前にあります。
注: C++11 ではpropagate_on_container_move_assignment
forstd::allocator
は ですがfalse_type
、これはtrue_type
for C++1y (y == 4 であることが望ましい) に変更されました。
One の場合、moved-fromvector
は常に空になります。
二:
allocator_traits<A>::propagate_on_container_move_assignment::value == false
&& get_allocator() == v.get_allocator()
(propagate_on_container_move_assignment
は に評価されfalse_type
、2 つのアロケータは等しいと比較されます)
この場合、ムーブ代入演算子はケース 1 と同じように動作しますが、次の例外があります。
- アロケータは移動割り当てされていません。
- このケースとケース 3 の間の決定は実行時に行われ、ケース 3 はより多くの を必要
T
とするため、ケース 2 も必要になりますが、ケース 2 は でこれらの追加の要件を実際には実行しませんT
。
ケース 2 では、moved-fromvector
は常に空になります。
三:
allocator_traits<A>::propagate_on_container_move_assignment::value == false
&& get_allocator() != v.get_allocator()
(propagate_on_container_move_assignment
は に評価されfalse_type
、2 つのアロケータは等しくありません)
v
この場合、実装はアロケータを移動して割り当てることも、リソースを転送することもできません*this
(メモリ バッファであるリソース)。この場合、移動代入演算子を効果的に実装する唯一の方法は次のとおりです。
typedef move_iterator<iterator> Ip;
assign(Ip(v.begin()), Ip(v.end()));
つまり、各個体T
を からv
に移動し*this
ます。は、利用可能な場合はとのassign
両方を再利用できます。たとえば、実装と同じものを持っている場合は、それぞれを からに割り当てることができます。これは である必要があります。移動代入演算子が必要ないことに注意してください。コピー代入演算子でも十分です。 just は右辺値から割り当て可能でなければならないことを意味します。capacity
size
*this
*this
size
v
T
v
*this
T
MoveAssignable
MoveAssignable
T
MoveAssignable
T
T
size
の*this
が十分でない場合は、で newT
を構築する必要があります*this
。これは である必要T
がありますMoveInsertable
。私が考えることができる正気のアロケーターはMoveInsertable
、 と同じものにMoveConstructible
要約されます。これは、右辺値から構築可能であることを意味しますT
( の移動コンストラクターの存在を意味するものではありませんT
)。
3 の場合、moved-fromvector
は一般に空にはなりません。移動元の要素でいっぱいになる可能性があります。要素に移動コンストラクターがない場合、これはコピー割り当てと同等になる可能性があります。ただし、これを強制するものは何もありません。実装者は、必要に応じて自由に追加の作業を行って実行し、空v.clear()
のままにすることができます。v
私は実装がそうしていることを認識していませんし、実装がそうする動機についても認識していません。しかし、私はそれを禁止するものは何も見ません。
v.clear()
David Rodríguez は、この場合、 GCC 4.8.1 が呼び出し、v
空 のままであると報告しています。libc++はそうではなく、v
空ではありません。どちらの実装も準拠しています。