ぼやきごと/2012-09-26/C++11:pimplイディオムにおけるデストラクタの default 指定 のバックアップソース(No.1)

#blog2navi()
*C++11:pimplイディオムにおけるデストラクタの default 指定 [#e3919b36]

皆さん、C++11でコーディングしてますか?~
私は昨日Fedora9のノートPCに gcc 4.7.2 を手動インストールして、ようやくC++11を書くことのできる環境を手に入れました。

C++11にはたくさんの新仕様がありますが、その中でも私が好きな仕様の一つが @code{default}; 指定です。~
C++のクラスでコンストラクタやデストラクタ等の記述を省略するとコンパイラがそれらを自動生成しますが、その「コンパイラに自動生成させる」ことを明示できる仕様です。

#code(c){{
class Foo
{
public:
    Foo() = default;
    ~Foo() = default;
    Foo(const Foo&) = default;
    Foo(Foo&&) = default;
    Foo& operator=(const Foo&) = default;
    Foo& operator=(Foo&&) = default;
};
}}

上述のコードにおいて @code{= default}; の記述がある宣言は書いても書かなくても生成されるクラスは同じです。~
しかし書かなかった場合、他の人がコードを読んだ時に「コンパイラに自動生成させようとしている」のか、「単に書き忘れた」のかが区別できません。~
敢えて @code{default}; 指定の宣言を記述することで、「私はこれらをコンパイラに自動生成させています」というアピールになります。~
また、他の人だけでなく、コンパイラにもその意図を明示的に伝えることができ、コンパイラによっては最適化の助けになる可能性もあるでしょう。

C++11では、特別な処理をさせる場合を除き、記述を省略したりましてや空っぽの実装を書いたりせずに @code{default}; 指定を積極的に用いるべきです。~
特にデストラクタについては、リソースの解放はメンバ変数に任せてヘッダファイルでの @code{default}; 指定を義務化するくらいのつもりで考えるべきでしょう。~
そしてそれらのメンバ変数を辿った先は、基本型、データ抽象クラス(@code{std::string}; 等)、各種スマートポインタクラスのいずれかに行き着くようにします。~
その辺りの話は次のWebページが参考になります。

-[[C++11時代におけるクラスの書き方 - イグトランスの頭の中(のかけら)>http://dev.activebasic.com/egtra/2011/12/02/451/]]

ただ、すべてのデストラクタをヘッダファイルで @code{default}; 指定できるかというと例外があります。

-デストラクタで(リソース解放以外の)何らかの処理を行いたい場合。
-リソースそのものを表すデータ抽象クラスやスマートポインタクラスの場合。
-デストラクタをインライン展開できない場合。

これらのうち、3番目の「デストラクタをインライン展開できない場合」に当てはまるものの1つにpimplイディオムがあります。

-[[pimplイディオムをscoped_ptrを使って実現するときの注意 - redboltzの日記>http://d.hatena.ne.jp/redboltz/20081003/1222991824]]

詳しくは上のWebページに書かれていますが、pimplイディオムを知っているという前提で簡単に説明します。

#code(c){{
// Var.h
#pragma once
#include <memory>

class Var
{
public:
    Var();
    ~Var() = default; // こう書くとコンパイルエラー!

private:
    // Var::Impl クラスの完全な定義は別ソースファイルに書く
    class Impl;
    std::unique_ptr<Impl> _impl;
};
}}

上述のコードのようにクラス宣言の中でデストラクタを @code{default}; 指定で宣言した場合、自動生成されるデストラクタはコンパイラによってインライン展開されます。~
するとその場で @code{_impl}; メンバのデストラクタも呼び出す必要があるため、 @code{std::unique_ptr<Impl>}; クラスの実体化が試みられますが、 @code{Var::Impl}; クラスは前方宣言しかされていないために実体化することができず、コンパイルエラーとなるわけです。~
これは @code{default}; 指定の代わりに空のデストラクタを書いても同じことで、解決するためにはデストラクタの実装を @code{Var::Impl}; クラスの完全な定義が見える場所に記述する必要があります。

#code(c){{
// Var.cpp
#include "Var.h"

// Var::Impl の定義
class Var::Impl
{
    // …略…
};

Var::Var() : _impl(new Impl()) { }

// Var のデストラクタの実装を Var::Impl の定義が見える場所に書く
Var::~Var() { }
}}

しかし、 @code{default}; 指定は何も宣言部でなければ書けないものではありません。~
むしろ @code{default}; 指定は「宣言として書くもの」ではなく、「実装(関数本体)の代わりとして書くもの」と考えた方がいいです。~
例えば上述のコードではデストラクタ @code{Var::~Var()}; の動作は既定のものでよいため、空の実装を書く代わりに @code{default}; 指定を用いて次のように書くこともできます。

#code(c,nomenu,nonumber){{
Var::~Var() = default;
}}

今回はこの「pimplイディオムを用いたクラスのデストラクタでも @code{default}; 指定できるよ!」ということだけ言いたかったのですが、何故かやたら長くなってしまいました…。~
ともあれ、 @code{default}; 指定は非常に有用ですので、C++11でコードを書く際には是非活用しましょう。

RIGHT:Category: &#x5b;[[プログラミング>ぼやきごと/カテゴリ/プログラミング]]&#x5d;&#x5b;[[C++>ぼやきごと/カテゴリ/C++]]&#x5d; - 2012-09-26 00:42:40
----
RIGHT:&blog2trackback();
#comment(above)
#blog2navi()