注入されたクラス名
注入されたクラス名はクラスのスコープ内でのそのクラスの名前です。
クラステンプレートでは、注入されたクラス名は現在のテンプレートを参照するテンプレート名または現在の実体化を参照するクラス名のいずれかとして使用できます。
[編集] 説明
クラススコープでは、現在のクラスの名前はパブリックなメンバの名前であるかのように扱われます。 これは注入されたクラス名と言います。 名前の宣言の地点はクラス定義の開き波括弧の直後です。
int X; struct X { void f() { X* p; // OK、 X は注入されたクラス名を参照します。 ::X* q; // エラー、名前探索は変数名を発見します (変数名が構造体名を隠蔽します)。 } };
他のメンバと同様に、注入されたクラス名は継承されます。 プライベートまたはプロテクテッド継承が存在する場合、間接的な基底クラスの注入されたクラス名は派生クラス内では最終的にアクセス不可能になる場合があります。
struct A {}; struct B : private A {}; struct C : public B { A* p; // エラー、注入されたクラス名 A はアクセス不可能です。 ::A* q; // OK、注入されたクラス名は使用しません。 };
[編集] クラステンプレートの場合
他のクラスと同様に、クラステンプレートは注入されたクラス名を持ちます。 注入されたクラス名はテンプレート名または型名として使用できます。
以下の場合、注入されたクラス名はクラステンプレートそれ自体のテンプレート名として扱われます。
-
<
が後に続く。 - テンプレートテンプレート仮引数に対応するテンプレート実引数として使用される。
- フレンドクラステンプレート宣言の複雑型指定子における最終識別子である。
そうでなければ、型名として扱われ、テンプレート名の後に <>
で囲まれたそのクラステンプレートのテンプレート引数が続いたものと同等です。
template <template <class, class> class> struct A; template<class T1, class T2> struct X { X<T1, T2>* p; // OK、 X はテンプレート名として扱われます。 using a = A<X>; // OK、 X はテンプレート名として扱われます。 template<class U1, class U2> friend class X; // OK、 X はテンプレート名として扱われます。 X* q; // OK、 X は型名として扱われます (X<T1, T2> と同等です)。 };
クラステンプレートの特殊化または部分特殊化のスコープ内で、注入されたクラス名が型名として使用されるとき、それはテンプレート名の後に <>
で囲まれたそのクラステンプレートの特殊化または部分特殊化のテンプレート引数が続いたものと同等です。
template<> struct X<void, void> { X* p; // OK、 X は型名として扱われます (X<void, void> と同等です)。 template<class, class> friend class X; // OK、 X はテンプレート名として扱われます (プライマリテンプレートの場合と同じです)。 X<void, void>* q; // OK、 X はテンプレート名として扱われます。 }; template<class T> struct X<char, T> { X* p, q; // OK、 X は型名として扱われます (X<char, T> と同等です)。 using r = X<int, int>; // OK、別の特殊化を表すために使用できます。 };
クラステンプレートまたはクラステンプレートの特殊化の注入されたクラス名は、スコープ内であればどこででも、テンプレート名または型名のいずれかとして使用できます。
template<> class X<int, char> { class B { X a; // X<int, char> を意味します。 template<class,class> friend class X; // ::X を意味します。 }; }; template <class T> struct Base { Base* p; }; template <class T> struct Derived: public Base<T> { typename Derived::Base* p; // Derived::Base<T> を意味します。 }; template<class T, template<class> class U = T::template Base> struct Third { }; Third<Derived<int>> t; // OK、デフォルト引数は注入されたクラス名をテンプレートとして使用します。
注入されたクラス名を探す名前探索は特定のケースで曖昧な結果になり得ます (例えば複数の基底クラスで見つかった場合)。 見つかった注入されたクラス名のすべてが同じクラステンプレートの特殊化を参照し、その名前がテンプレート名として使用された場合、その参照はクラステンプレートの特殊化ではなくクラステンプレート自体を参照し、曖昧にはなりません。
template <class T> struct Base {}; template <class T> struct Derived: Base<int>, Base<char> { typename Derived::Base b; // エラー、曖昧です。 typename Derived::Base<double> d; // OK。 };
[編集] 注入されたクラス名とコンストラクタ
コンストラクタは名前を持ちませんが、コンストラクタの宣言および定義では囲っているクラスの注入されたクラス名がコンストラクタを表すとみなされます。
修飾名 C::D
において、
- 名前探索が関数の名前を無視しない、かつ
- クラス
C
のスコープ内でD
の名前探索がその注入されたクラス名を発見する
場合、その修飾名は常に C
のコンストラクタを表すとみなされます。 そのような名前はコンストラクタの宣言 (例えばフレンドコンストラクタ宣言、コンストラクタテンプレートの特殊化、コンストラクタテンプレートの実体化、またはコンストラクタの定義) で、またはコンストラクタを継承するために (C++11以上)のみ使用できます。
struct A { A(); A(int); template<class T> A(T) {} }; using A_alias = A; A::A() {} A_alias::A(int) {} template A::A(double); struct B : A { using A_alias::A; }; A::A a; // エラー、 A::A は型ではなくコンストラクタを表すとみなされます。 struct A::A a2; // OK、「A a2;」と同じです。 B::A b; // OK、「A b;」と同じです。