実践C++入門講座こんにちは。今回は最後の特殊メンバ関数ムーブ・コンストラクタとその使い方について解説します。特殊メンバ関数は常に自動生成されるわけではありません。自動生成されないのはどんな時か、またその理由について説明します。1.ムーブ・コンストラクタ前回、前々回と特殊メンバ関数について解説してきましたが、その最後のものがムーブ・コンストラクタです。前回のサンプルにムーブ・コンストラクタを入れてみます。(解説の都合で少し内容を変えています。)ムーブ・コンストラクタの構文は自分と同じ型の右辺値参照を1つだけ受け取るコンストラクタです。内容はムーブ・セマンティクスのお約束に従い「ムーブ」と言える処理にします。下記の例では、管理しているmFooの所有権とポインタをムーブしています。mFoo;も不要です。通常通りクラス型の変数を定義する際に、例えば他の関数の戻り値と同じものをコンストラクトする時が典型的な使い方と思います。関数の戻り値ですから一時オブジェクト(右辺値)となるため、その文が終わると解放されます。関数で作られたデータをなるべく有効活用するため、ムーブしたいです。今までのBarクラスを使ったサンプルです。aBarデストラクト(mFooは移動済み)Wandboxで試してみるまた、関数がクラス・オブジェクトを戻り値とする場合にも使うことができます。返却するクラス・オブジェクトはreturn文で生成されるため、そのコンストラクト時に用いると効率が上がりそうです。実は優秀なコンパイラは戻り値も最適化の対象としており、ムーブよりも更に効率が良い場合が多いです。clangは該当します。)を使う時は「成り行き(コンパイラ)」に任せた方が性能が上がります。詳しいことは次回解説します。Optimization)です。2.ところで右辺値参照は左辺値ですここが個人的に一番苦しみました。右辺値参照は「右辺値」を参照しているので右辺値のように感じ取れますし、字面も右辺値っぽいですね。以前解説したように右辺値は値です。値は容器の中身ですね。コンピュータの実装上例えば1+1を計算した結果をどこかに記録しています。それが保持されている容器が一時オブジェクトです。一時オブジェクトは一時的な領域で直ぐに破棄されますが、メモリ領域なのでアドレスを持つ「容器」です。(厳密にはちょっと違いますが、ややこしい話は割愛します。)その一時オブジェクト(メモリ領域)を参照している右辺値参照は左辺値(容器)なのです。そもそも、右辺値参照は参照先のデータを空へ変更することが目的ですから、当然その中身を変更できないといけません。右辺値参照が左辺値であることは当然のことなのです。従って、右辺値参照(左辺値)を更に右辺値参照で受け取りたい場合は、std::moveして右辺値へ偽装する必要があります。そのサンプルを「ムーブ・コンストラクタの使用例」に追加します。aBarデストラクト(ここで1のFooデストラクト)quxは、右辺値参照iBarで引数を受け取ります。右辺値参照なので他の関数の戻り値等の右辺値を受け取ることができます。一度受け取ってしまったiBarは上述の通り左辺値となります。これを再度Barのムーブ・コンストラクタへ引き渡したい場合は、再度右辺値を偽装するためにstd::move必要があります。0へ変更してみて下さい。0にした時、コピー・コンストラクタがないのでコピーできないと書きました。確かにコピー・コンストラクタを書いてはいないのですが、コピー・コンストラクタは自動生成される筈ですね。実は特殊メンバ関数は常に自動生成されるわけではありません。第34回で解説したように所有権を管理しているようなオブジェクトをコピーするとデストラクト時に異常が発生します。自動生成される特殊メンバ関数でこのようなことが普通に起きるとあまりうれしくないですね。このような問題が起きにくいように、特定のケースでは自動生成されません。ルールとしては、以下の通りです。ユーザがデストラクタを定義した場合ムーブ特殊メンバ関数(ムーブ・コンストラクタとムーブ代入演算子)は自動生成されません。コピー特殊メンバ関数(コピー・コンストラクタとコピー代入演算子)は自動生成されますが、非推奨。(将来自動生成されなくなる予定)デストラクタが定義されているということは、デストラクタで何らかのリソースを解放する場合が多いです。その時、そのクラスが管理するリソースを自動生成されるムーブ特殊メンバ関数で適切にムーブできない可能性が高いためです。コピー特殊メンバ関数も同様なのですが、C++11より前は自動生成されていたので互換性のため自動生成されます。ユーザが1つでもコピー特殊メンバ関数を定義した場合コピー/ムーブ特殊メンバ関数は自動生成されません。コピーとムーブの両方に対応するクラスは特殊なので、自動生成で賄えない可能性が高いためと思われます。ユーザが1つでもムーブ特殊メンバ関数を定義した場合コピー/ムーブ特殊メンバ関数は自動生成されません。コピーとムーブの両方に対応するクラスは特殊なので、自動生成で賄えない可能性が高いためと思われます。ユーザがデフォルト以外のコンストラクタを定義した場合デフォルト・コンストラクタは自動生成されません。シングルトンと呼ばれる良く使われるクラスのパターンがあります。シングルトンは、オブジェクトを1つだけしか生成できないことを保証するクラスです。それがコピーできてしまうとオブジェクトが複数存在します。ムーブにより管理している領域を移動しますが、クラス・オブジェクト自体はコピーされるので複数のオブジェクトを生成できます。このような時は、自動生成されては困る特殊メンバ関数にdelete指定します。(下記の例はシングルトンではありません。ユーザが空のデストラクタを書くことが有ります。第27回目で解説したように動的ポリモーフィズムを使う場合には仮想デストラクタを定義するべきです。その際デストラクタで処理したいことがないことも少なくありません。そのような時は空の仮想デストラクタを定義するでしょう。このケースでは、コピー特殊メンバ関数やムーブ特殊メンバ関数は自動生成されるもので良い場合も少なくありません。このような時は、default指定して自動生成されるものと同じ特殊メンバ関数を生成させることができます。(因みに、特殊メンバ関数の自動生成ルールを判定する際、default指定して強制的に自動生成させた場合も「ユーザが定義した」ものとして扱われます。)少し無理矢理なサンプルですが、Handleクラスを作ってみました。これはハンドル値を管理するクラスで、ハンドル値には無効値(kInvalidHandle)を定義しています。実際に使う場面はあまりないような気もしますが、コピー・コンストラクタとムーブ・コンストラクタの両方を定義しています。ムーブ・コンストラクタを定義するとコピー・コンストラクタは自動生成されなくなるので、default指定しています。}};このクラスを基底クラスで管理し、動的ポリモーフィズムで使う場合を想定してみました。(派生クラスDerivedは手抜きでごめんなさい。iRhs)が呼ばれ、メンバ変数mValueコピー後ムーブ元のaBaseのmHandle.mValueにkInvalidが設定される。次に、Wandboxのソース31行目にあるBaseのムーブ・コンストラクタをコメントアウトしてみて下さい。iRhs)が呼ばれる。これは自動生成と同じものなのでメンバ変数を単純にコピーする。4.まとめ今回はムーブ・コンストラクタ、特殊メンバ関数が自動生成されない時、特殊メンバ関数の働き方について解説してみました。自動生成とdelete/defaultの動きについては分かりにくいのでサンプルを使って解説してみました。理解の助けになれば幸いです。次回は、今回解説できなかったムーブや特殊メンバ関数周りの話題を解説します。コメントをキャンセルメールアドレスが公開されることはありません。テクノロジーの登録商標です。technology.。