5.1.3. コーディング技法

ポリシークラスとホストクラスは、言語に支援された実装形態ではない。標準でない実装手段を用いる場合は、言語によるプログラミングサポートが乏しくなり、設計の意図をコードで表明できない箇所が発生する。コード化できないルールは言語処理系で検証できないので、文書として明示し、人手あるいは補助ツールで注意深く守るようにしなければならない。

ここでは、ホストクラスがポリシークラスに対して要求するルール、あるいはその逆のルールのうち、コード化可能なものを列挙する。

ホストクラス側でコード化可能なポリシークラスに対するルールは次の通り。

公開インタフェースの追加を許可する/禁止する

許可する場合、public継承を用いる。禁止する場合、publicでない継承あるいは他の手段を用いる。

型名を要求する

目的の名前を型名として使用する。例えば次のようにする:

typedef typename policy::target something;
定数を要求する

目的の名前を定数として使用する。例えば次のようにする:

enum { something = policy::target };
メンバ関数を要求する

非静的メンバ関数から目的の関数を呼び出す。例えば次のようにする:

void func() { policy::target(); }

policy::targetが非静的か静的か、多重定義されているか否かは問えない。

例外仕様は問えない。だが、例えば次のようにすると:

void func() throw() { policy::target(); }

policy::targetの例外仕様がthrow()ではないとき、処理系と状況によってはコンパイラによる警告を期待できる。

非静的const/volatileメンバ関数または静的メンバ関数を要求する

目的のcv修飾子を持つ非静的メンバ関数から目的の関数を呼び出す。例えば次のようにする:

void func() const { policy::target(); }

policy::targetが非静的か静的か、多重定義されているか否かは問えない。

静的メンバ関数を要求する

静的メンバ関数から目的の関数を呼び出す。例えば次のようにする:

static void func() { policy::target(); }

policy::targetが多重定義されているか否かは問えない。

デフォルト/コピー/その他のコンストラクタを要求する

要求する形態のコンストラクタなどを呼び出す。

代入演算子を要求する

要求する形態のpolicy::operator=を呼び出す。

データメンバを要求する

メンバ関数と同様に行う。

名前を要求する

using宣言を用いる。例えばusing policy::target;などとする。ポリシークラスでその名前が関数として多重定義されていれば、多重性もそのまま引き継がれる。言語規約上、アクセス権限の降格(例えばpublicからprivateへの変更)はできない。アクセス権限の据え置きと昇格はできる。

名前を隠蔽する

同名の定義を行う。例えば次のようにする:

void func() { policy::func(); }

これにより、policy::funcの関数シグネチャ、アクセス権限、多重性はホストクラス外部からは参照不能になる。

名前を禁止する

同名かつ無効な定義を行う。例えば次のようにする:

private:
  typedef void target;

これにより、ポリシークラスが名前targetをホストクラスの公開インタフェースへと追加することができなくなる。ただし、ホストクラス内からpolicy::targetを参照することは禁止できない。

ポリシークラス側でコード化可能なホストクラスに対するルールは次の通り。

ポリシークラス側でコード化可能なホストクラスに対するルール

派生クラスになることを要求する

デストラクタをprotectedにする。

公開インタフェースを追加する

追加する公開インタフェースをpublicメンバとして定義する。

ホストクラスに対するルールではないが、ポリシークラスは抽象クラスではないため、デストラクタは非仮想にする必要がある。

通常のクラスと同様、非公開でよいものはprivateにする。ホストクラスから要求された機能はprotectedにする。ただし、ホストクラスから要求された型名で、ホストクラス外で使用する必要があるもの(例えばホストクラスがポリシークラスを継承する際にテンプレートパラメータに使用する型名)については、publicにする。

また、例外安全性のために、例外を投げないメンバ関数swapを定義しておくと便利である。空クラスでなければ非静的メンバ関数、空クラスであれば静的メンバ関数として定義するとよい。 以下に例を示す。

例 5.4. メンバ関数swapの定義

class some_policy
{
public:
  void swap(some_policy& rhs) throw() { /* ... */ }
};

class empty_policy
{
public:
  static void swap(empty_policy&) throw() {}
};

template <class P>
class host
  : public P
{
public:
  void swap(host& rhs) throw()
    { P::swap(rhs); }
  host& operator=(host rhs)
    { swap(rhs); return *this; }
};

ホストクラスhostは、ポリシークラスのswapが静的か非静的かを区別する必要はない。