注入されたクラス名
注入されたクラス名とは、そのクラスのスコープ内における、修飾されていないクラス名のことです。
クラステンプレートにおいて、注入されたクラス名は、現在のテンプレートを参照するテンプレート名としても、現在のインスタンス化を参照するクラス名としても使用できます。
目次 |
[編集] 説明
クラスのスコープ内では、現在のクラスのクラス名または現在のクラステンプレートのテンプレート名が、あたかもpublicなメンバ名であるかのように扱われます。これは「注入されたクラス名」と呼ばれます。この名前の宣言点は、クラス(テンプレート)定義の開始ブレースの直後です。
int X; struct X { void f() { X* p; // OK, X is an injected-class-name ::X* q; // Error: name lookup finds a variable name, which hides the struct name } }; template<class T> struct Y { void g() { Y* p; // OK, Y is an injected-class-name Y<T>* q; // OK, Y is an injected-class-name, but Y<T> is not } };
他のメンバと同様に、注入されたクラス名も継承されます。privateまたはprotected継承の場合、間接的な基底クラスの注入されたクラス名が、派生クラスでアクセス不能になる可能性があります。
struct A {}; struct B : private A {}; struct C : public B { A* p; // Error: injected-class-name A is inaccessible ::A* q; // OK, does not use the injected-class-name };
[編集] クラステンプレート内
クラステンプレートの注入されたクラス名は、テンプレート名または型名として使用できます。
以下のケースでは、注入されたクラス名はクラステンプレート自体のテンプレート名として扱われます。
<が後に続く場合。- テンプレートテンプレート引数として使用される場合。
- friendクラステンプレート宣言の詳細型指定の最後の識別子である場合。
それ以外の場合は、型名として扱われ、<>で囲まれたテンプレートパラメータを持つクラステンプレートのテンプレート名と同等になります。
template<template<class, class> class> struct A; template<class T1, class T2> struct X { X<T1, T2>* p; // OK, X is treated as a template-name using a = A<X>; // OK, X is treated as a template-name template<class U1, class U2> friend class X; // OK, X is treated as a template-name X* q; // OK, X is treated as a type-name, equivalent to X<T1, T2> };
クラステンプレートの特殊化または部分特殊化のスコープ内では、注入されたクラス名が型名として使用される場合、<>で囲まれたクラステンプレート特殊化または部分特殊化のテンプレート引数を持つテンプレート名と同等になります。
template<> struct X<void, void> { X* p; // OK, X is treated as a type-name, equivalent to X<void, void> template<class, class> friend class X; // OK, X is treated as a template-name (same as in primary template) X<void, void>* q; // OK, X is treated as a template-name }; template<class T> struct X<char, T> { X* p, q; // OK, X is treated as a type-name, equivalent to X<char, T> using r = X<int, int>; // OK, can be used to name another specialization };
クラステンプレートまたはクラステンプレート特殊化の注入されたクラス名は、スコープ内であればテンプレート名としても型名としても使用できます。
template<> class X<int, char> { class B { X a; // meaning X<int, char> template<class, class> friend class X; // meaning ::X }; }; template<class T> struct Base { Base* p; // OK: Base means Base<T> }; template<class T> struct Derived : public Base<T*> { typename Derived::Base* p; // OK: Derived::Base means Derived<T>::Base, // which is Base<T*> }; template<class T, template<class> class U = T::template Base> struct Third {}; Third<Derived<int>> t; // OK: default argument uses injected-class-name as a template
注入されたクラス名を見つける名前探索は、特定のケース(例えば、複数の基底クラスで見つかった場合)で曖昧さを生じさせる可能性があります。見つかったすべての注入されたクラス名が同じクラステンプレートの特殊化を参照しており、その名前がテンプレート名として使用されている場合、その参照は特殊化ではなくクラステンプレート自体を参照し、曖昧ではありません。
template<class T> struct Base {}; template<class T> struct Derived: Base<int>, Base<char> { typename Derived::Base b; // error: ambiguous typename Derived::Base<double> d; // OK };
[編集] 注入されたクラス名とコンストラクタ
コンストラクタは名前を持ちませんが、囲むクラスの注入されたクラス名は、コンストラクタの宣言と定義においてコンストラクタを命名するものと見なされます。
修飾名 `C::D` において、もし
- 名前探索が関数名を無視せず、かつ
- クラス `C` のスコープ内での `D` の探索がその注入されたクラス名を見つけた場合
その修飾名は常に `C` のコンストラクタを命名するものと見なされます。そのような名前は、コンストラクタの宣言(例: friendコンストラクタ宣言、コンストラクタテンプレート特殊化、コンストラクタテンプレートインスタンス化、またはコンストラクタ定義)またはコンストラクタの継承(since 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; // Error: A::A is considered to name a constructor, not a type struct A::A a2; // OK, same as 'A a2;' B::A b; // OK, same as 'A b;'
[編集] 不具合報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 1004 | C++98 | 注入されたクラス名は テンプレートテンプレート引数にできませんでした。 |
この場合、クラス テンプレート自体を参照する。 |
| CWG 2637 | C++98 | テンプレートID全体が注入されたクラス名になる可能性がありました。 | テンプレート名のみが |