質問はコメントによって回答されたと見なされていますが、以下にいくつかの詳細を提供します。私の答えはC++11(ISO / IEC 14882-2011)のみに基づいていることに注意してください。
パート1:テンプレートパラメーターをネストされたメンバーテンプレートのテンプレートパラメーターとして再利用できますか?
テンプレートパラメータのステータスに関する標準には2つの重要なステートメントがあります(具体的には、タイプパラメータ-質問に関連する唯一の種類です)。最初のステートメントは、それらがtypedef-namesと同じステータスを持っていると説明しています。
(§14.1/ 3)識別子が省略形に従わない型パラメータは、その識別子をテンプレートのスコープ内のtypedef-name(クラスまたはtypenameで宣言されている場合)またはtemplate-name(templateで宣言されている場合)として定義します宣言。[...]
そして、typedef-namesの可能な再宣言に関して、次のようになります。
(§7.1.3/ 6)特定のスコープでは、typedef指定子を使用して、そのスコープで宣言されたタイプの名前を再定義して、別のタイプを参照することはできません。[...]
(注:上記のルールはtypedef指定子の使用にのみ適用されるようですが、エイリアス宣言(§7.1.3/ 2)とテンプレートパラメーター宣言(§14.1/ 3)を使用してtypedef-namesを宣言することもできます。つまり、上記のルールは、同じスコープ内でtypedef-nameを再宣言するためのエイリアス宣言または実際のテンプレートパラメータの使用を明示的に除外していませんが、それは明らかに意図された意味です。言い回しは「typedef-name宣言なし」である必要があります。 「typedef指定子は使用しない」の代わりに「使用する」)。
これが意味することは、これを行うことができないということです。
{
typedef int T;
typedef float T;
}
2番目の宣言は、最初に宣言されたのと同じスコープで発生するためT
です。ただし、これは次のとおりです。
{
typedef int T;
{
typedef float T;
}
}
T
2番目の宣言はブロックスコープ内にあり(最初の宣言はまだ有効ですが)、最初に宣言されたスコープではないため、は完全に合法的に上記のルールに従いT
ます。
上で引用した§14.1/3のため、ルールはテンプレートパラメータ宣言にも適用されると想定する必要があります。
template <typename X> template <typename X>
struct Outer<X>::Inner<X> {
};
同じスコープ内で同じtypedef-nameを2回宣言することを意味するという単純な理由でさえ、違法です。
しかし、次のような場合
template <typename X>
struct Outer {
template <typename X>
struct Inner {
};
};
template <typename X>
の2番目の宣言はネストされたスコープに適用されると主張する人もいるかもしれません。幸い、この標準では、テンプレートパラメータのステータスに関する次の2番目のステートメントが提供されています。
(§14.6.1/ 6)テンプレートパラメータは、そのスコープ(ネストされたスコープを含む)内で再宣言してはなりません。テンプレートパラメータは、テンプレート名と同じ名前であってはなりません。[例:
template<class T, int i> class Y {
int T; // error: template-parameter redeclared
void f() {
char T; // error: template-parameter redeclared
}
};
template<class X> class X; // error: template-parameter redeclared
—終了例]
明確に述べられているように、宣言スコープ内の任意のtypedef-nameに適用される再宣言なしのルールは、テンプレートパラメーターの場合にもネストされたスコープに適用されます。
これは、ルールが実際に役立つと思う理由を動機付ける例です。検討:
template <typename T1>
struct Outer
{
static const int outerID = 5;
template <typename T2>
struct Inner
{
int id1() { return Outer<T1>::outerID; }
int id2() { return Outer::outerID; }
int id3() { return outerID; }
};
};
内部テンプレートの3つの関数はすべて、外部クラスの同じ静的メンバーを参照しますが、3つの異なる方法で参照されます。§14.6.2.1/4では、現在のインスタンス化を参照していると解釈される必要があるため、これをid2()
行います。id3()
Outer::outerID
outerID
Outer<T1>::outerID
内側のテンプレートのテンプレートパラメータを、外側のテンプレートとT1
同じように、に置き換えると、意味id1()
が変わります(内側のテンプレートでOuter<T1>
の定義が何であれ、参照されるため)が、最も自然に–所属するテンプレートの現在のインスタンス化を引き続き参照してください。したがって、およびとは異なる値を返す可能性があり、これは最も厄介です。T1
id2()
id3()
outerID
id1()
id2()
id3()
パート2:メンバーテンプレートの部分的な特殊化では、メンバーのテンプレートパラメーターを囲んでいるクラスのテンプレート引数として使用できますか?その逆も可能ですか?
質問によって対処される別の問題は、メンバーテンプレートの専門化またはクラス外の定義にあるかどうかです。
template <typename A> template <typename B>
struct Outer<A>::Inner<B> {
// ...
};
外側のテンプレート(つまりこの場合)のテンプレート引数リストは、内側のテンプレート(つまり<A>
この場合)に定義されたパラメーターを利用できB
、その逆も可能です。
最初に、質問で与えられた2つのパラメーターが同一である特殊なケースについて考えてみましょう。
template <typename A> template <typename A>
struct Outer<A>::Inner<A> {
// ...
};
再宣言の問題により、パート1でこれを除外しましたが、この構文の意図された意味を考慮することができます。つまり、内部クラステンプレートの明示的な特殊化を定義します。ここで、テンプレート引数は次のように想定されます。外側のテンプレートと同じです。構文的に正しい書き方は
template <typename A> template <>
struct Outer<A>::Inner<A> {
// ...
};
つまり、2番目のパラメータリストは空になります。残念ながら、これはメンバーテンプレートの明示的な特殊化に相当します。これは、囲んでいるテンプレートも明示的に特殊化されていない限り違法です(§14.7.3/ 16)。
ただし、2つ以上のパラメーターを持つメンバーテンプレートの明示的な特殊化ではなく、部分的な特殊化を検討する場合、宣言は有効になります。
// Primary declaration
template <typename A>
struct Outer {
template <typename A, typename B>
struct Inner {
// ...
};
};
// Partial specialization of member template
template <typename A> template <typename B>
struct Outer<A>::Inner<B,A> {
// ...
};
A
これで、内部テンプレートの特殊な2番目の引数として、囲んでいるテンプレートのテンプレート引数を使用しました。外部クラスのテンプレート引数と内部クラスの2番目のテンプレート引数に同じデータ型を使用するテンプレートのインスタンス化。例:
Outer<int>::Inner<float,int> myvar;
上記で定義されたスペシャライゼーションをインスタンス化します。
したがって、メンバーテンプレートのテンプレート引数リストで囲んでいるクラスのテンプレートパラメータを使用しても問題はありません。その理由は、Inner<B,A>
評価A
されるまでに、スコープレベルで定義されたtypedef-nameのステータスがすでにあるためですOuter
。
しかし、それを逆に行う、例えば
template <typename A> template <typename B>
struct Outer<B>::Inner<B,A> {
// ...
};
はスコープB
のtypedef-nameのみであるため、機能しません。Inner
標準は次のように述べています。
(§14.5.2/ 1)[...]クラステンプレート定義の外部で定義されたクラステンプレートのメンバーテンプレートは、クラステンプレートのtemplate-parametersの後に、メンバーのtemplate-parametersを付けて指定する必要があります。レンプレート。[例:
template<class T> struct string {
template<class T2> int compare(const T2&);
template<class T2> string(const string<T2>& s) { /∗ ... ∗/ }
};
template<class T> template<class T2> int string<T>::compare(const T2& s) {
}
—終了例]
これは、2つのテンプレートパラメータリスト(一方の後にもう一方が続く)が別々に保持され、テンプレートパラメータの1つの結合リストとは見なされないことを意味すると解釈します。したがって、
template <typename A> template <typename B>
struct Outer<B>::Inner<B,A>
の解釈はOuter<B>
2番目のテンプレートパラメータリストを利用できず、B
未定義になりますが、前のケースでは
template <typename A> template <typename B>
struct Outer<A>::Inner<B,A>
の一部であるスコープで宣言されたtypedef-nameのステータスがあるため、の解釈Inner<B,A>
が可能です。つまり、と同じように正常に解釈されます。A
Inner
Inner<B,Outer::A>
Inner<B,Outer<A>::A>