名前付きテンプレートパラメータ(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_args、policy_selector、allocation_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_is、construction_isおよびiterator_isは、default_policiesの公開派生クラスで、それぞれのクラス名に対応するポリシーの名前を上書きする。default_policy_argsも同じくdefault_policiesの公開派生クラスだが、ポリシーの名前の上書きは行わない。default_policy_argsはホストクラスのテンプレートパラメータのデフォルト値として用いられる。
policy_selectorは、ホストクラスのテンプレートパラメータから目的のポリシーの名前を得るための名前解決を行う。同一クラスを継承するための緩衝材としてdiscriminatorを、多重継承による曖昧性を回避するために仮想継承を用いる。
結果として、
policy_selector<P1, P2, P3>::allocation_policy
などという記述で目的のポリシーの名前を得ることができる。P1、P2およびP3がすべてホストクラスでのデフォルト値(default_policy_args)であれば、allocation_policyはdefault_policiesでの定義に従う。P1、P2およびP3のいずれかにallocation_isが指定されていれば、allocation_policyはallocation_isで上書きされた定義に従う。