こんにちは。この頃はじめじめして嫌な季節の訪れを感じますね。
今回は、ムーブ後のオブジェクトがどのような状態になるのか、あるいは、ムーブの処理を書くときにどのような状態を保証するべきか、ということを長々と書いていきたいと思います。
また、途中紹介する規格の文章はわたしの解釈で日本語訳したものであり(直訳ではありません)、正式な引用ではありません。引用元を同時に載せますので、原文を確認する場合はそちらを参照ください。
ムーブする、というのはその変数をもう使わないからムーブする、というのが基本的な考え方ではありますが、ムーブした後でも操作が加えられることはあります。例えば、デストラクタがそうですね。ムーブされた後のオブジェクトにもデストラクタは走ります。或いは、汎用的なswapを書こうとしたら、再度その変数へオブジェクトを割り当てることになるでしょう。
もう使わないからといっても、最低限の動作は保証することが望ましいのです。
ムーブ後の変数の状態について、規格では要求はありません。ただし、標準ライブラリに渡される場合はムーブされたされないに拘らずその要求を満たす必要があります。(N3337 17.6.3.1 Table 20 [NOTE])
それでは、結局わたしたちはどの程度の保証をムーブ後のオブジェクトにしたらよいのでしょうか。
標準ライブラリに定義されるクラスのムーブ後の状態は、有効だが未規定とされています。(一部例外としてより厳しい条件を持つ物もある)
標準ライブラリのクラス群が満たすべきムーブ後の保証なら、倣っておいて損はないんじゃないじゃないか、ということで、有効だが未規定という状態を見てみましょう。
オブジェクトの有効性が保証されていて、かつオブジェクトに対する操作が定義通り行われる、ということを除き一切の状態が保証されない状態。
例えば、std::vector<int>のxというオブジェクトが有効だが未規定の状態だとすると、x.empty()は無条件に呼び出すことができるし、もしx.emptyがfalseでなければx.front()を呼ぶこともできる。(N3337 17.3.26)
なるほどなるほど。
有効だが未規定の状態であればoperator=やassign()などは使えて然るべきですし、ムーブを利用したswapも安全に使えると考えてよいでしょう。デストラクタもちゃんと動くはずです。
余談ですが、ムーブ後のオブジェクトのデストラクタに関して、C++03とC++11の間に重大な互換性の問題があります。
C++03ではコピーが呼ばれていたが、C++11では暗黙的にムーブが呼ばれるような場面において、C++11で新たに生成される暗黙のムーブがメンバ変数を自動的に全てムーブしてしまい、ムーブを考慮しないC++03のデストラクタがムーブ後のメンバ変数を参照してしまう、という重大なものです。
この場合当該クラスのムーブ後のオブジェクトは有効だが未規定という状態を満たさないので注意してください。
またC++11でこのようなミスを起こすことを防ぐために、はデストラクタを書くときは必ずムーブやコピーの処理を宣言しましょう。(定義するか、default指定するか、delete指定するかしましょう)