5.6. 名前付きテンプレートパラメータ

名前付きテンプレートパラメータ(NTP: Named Template ParametersまたはNamed Template Arguments)は、名前を用いてクラステンプレートにテンプレートパラメータを与えられるようにする技法である。いくつかの実装手段があると考えられるが、ここではC++ Templates[Vandevoorde2003]に記載されている方法について述べる。

複数のポリシークラスを取るホストクラスがあったとき、ホストクラスを実体化させる際には、デフォルトテンプレートパラメータを除くすべてのテンプレートパラメータを正しい順序で指定しなければならない。以下に示すコードは、3つのポリシークラスを取るホストクラスと、ホストクラスを用いるクライアントコードの例である。

例 5.14. 名前付きテンプレートパラメータを用いないホストクラス

template <
  typename T,
  class AP = AP_default,
  class CP = CP_default,
  class IP = IP_default
>
class host
  : public AP, public CP, public IP
{
private:
  typedef typename AP allocation_policy;
  typedef typename CP construction_policy;
  typedef typename IP iterator_policy;
public:
  ...
};

void foo()
{
  typedef unsigned int T;
  host<T, myAP> host_myAP;
  host<T, AP_default, CP_default, myIP> host_myIP;
}

クラステンプレートhostは、割り当てポリシー、構築ポリシーおよび反復子ポリシーの3つのポリシーをテンプレートパラメータに取るホストクラスである。

クライアントコードfooは、hostの2つの実体を定義している: host_myAPは割り当てポリシーにmyAPを指定して残りのポリシーをデフォルトのままにしたもの、host_myIPは反復子ポリシーにmyAPを指定して残りのポリシーをデフォルトのままにしたものである。host_myAPを定義する際には、第2テンプレートパラメータに目的の割り当てポリシーを指定すればよい: 以降のテンプレートパラメータはデフォルトのままでよいので、省略できる。しかし、host_myIPを定義する際には、第4テンプレートパラメータに目的の反復子ポリシーを指定するために、それよりも前に位置する割り当てポリシーと構築ポリシーを明記しなければならない。

名前付きテンプレートパラメータを用いると、このような繁雑さを解消できる。以下に、同様のコードを名前付きテンプレートパラメータを用いて記述した例を示す(default_policy_argspolicy_selectorallocation_isなどについては後述する)。

例 5.15. 名前付きテンプレートパラメータを用いたホストクラス

template <
  typename T,
  class P1 = default_policy_args,
  class P2 = default_policy_args,
  class P3 = default_policy_args
>
class host
  :
  public policy_selector<P1, P2, P3>::allocation_policy,
  public policy_selector<P1, P2, P3>::construction_policy,
  public policy_selector<P1, P2, P3>::iterator_policy
{
private:
  typedef typename policy_selector<P1, P2, P3>::allocation_policy allocation_policy;
  typedef typename policy_selector<P1, P2, P3>::construction_policy construction_policy;
  typedef typename policy_selector<P1, P2, P3>::iterator_policy iterator_policy;
public:
  ...
};

void foo()
{
  typedef unsigned int T;
  host<T, allocation_is<myAP> > host_myAP;
  host<T, iterator_is<myIP> > host_myIP;
}

名前付きテンプレートパラメータを用いない場合と異なり、host_myIPを定義する際には、目的の反復子ポリシーを指定するだけでよい。また、2つ以上のポリシーを指定する際には、

host<T, iterator_is<myIP>, construction_is<myCP> >

などと任意の順序でテンプレートパラメータを記述できる。

名前付きテンプレートパラメータの残りの実装は次の通り。

例 5.16. 名前付きテンプレートパラメータの実装

struct default_policies
{
  typedef allocation_policy AP_default;
  typedef construction_policy CP_default;
  typedef iterator_policy IP_default;
};

template <class P>
struct allocation_is : virtual default_policies
  { typedef P allocation_policy; };

template <class P>
struct construction_is : virtual default_policies
  { typedef P construction_policy; };

template <class P>
struct iterator_is : virtual default_policies
  { typedef P iterator_policy; };

struct default_policy_args : virtual default_policies;

template <class Base, int D>
struct discriminator : public Base {};

template <class P1, class P2, class P3>
struct policy_selector
  : discriminator<P1, 1>, discriminator<P2, 2>, discriminator<P3, 3> {};

default_policiesは、デフォルトで選択されるポリシーの名前を定義するクラスである。allocation_isconstruction_isおよびiterator_isは、default_policiesの公開派生クラスで、それぞれのクラス名に対応するポリシーの名前を上書きする。default_policy_argsも同じくdefault_policiesの公開派生クラスだが、ポリシーの名前の上書きは行わない。default_policy_argsはホストクラスのテンプレートパラメータのデフォルト値として用いられる。

policy_selectorは、ホストクラスのテンプレートパラメータから目的のポリシーの名前を得るための名前解決を行う。同一クラスを継承するための緩衝材としてdiscriminatorを、多重継承による曖昧性を回避するために仮想継承を用いる。

結果として、

policy_selector<P1, P2, P3>::allocation_policy

などという記述で目的のポリシーの名前を得ることができる。P1P2およびP3がすべてホストクラスでのデフォルト値(default_policy_args)であれば、allocation_policydefault_policiesでの定義に従う。P1P2およびP3のいずれかにallocation_isが指定されていれば、allocation_policyallocation_isで上書きされた定義に従う。