注入类名是在类的作用域内该类自身的名字。

类模板中,注入类名能用作指代当前模板的模板名,或指代当前实例化的类名。

解释

在类作用域中,当前类的名字被当做它如同是一个公开成员名一样;这被称为注入类名(injected-class-name)。该名字的声明点紧跟类定义的开花括号之后。

  1. int X;
  2. struct X {
  3. void f() {
  4. X* p; // OK:X 指代注入类名
  5. ::X* q; // 错误:名称查找找到变量名,它隐藏 struct 名
  6. }
  7. };

与其他成员类似,注入类名可被继承。在私有或受保护继承的场合,可能导致某个间接基类的注入类名在派生类中最后变得不可访问。

  1. struct A {};
  2. struct B : private A {};
  3. struct C : public B {
  4. A* p; // 错误:注入类名 A 不可访问
  5. ::A* q; // OK:不使用注入类名
  6. };

在类模板中

与其他类相似,类模板也拥有注入类名。其注入类名可被用作模板名或类型名。

下列情况下,注入类名被当做类模板自身的模板名:

  • 它后面跟着 <
  • 它被用作对应某个模板模板形参的模板实参
  • 它是某个友元类模板声明的详述类型说明符中的最后标识符。

否则,它被当做类型名,并等价于模板名后随环绕于 <> 中的该类模板的各个模板形参。

  1. template <template <class, class> class> struct A;
  2.  
  3. template<class T1, class T2>
  4. struct X {
  5. X<T1, T2>* p; // OK:X 被当做模板名
  6. using a = A<X>; // OK:X 被当做模板名
  7. template<class U1, class U2>
  8. friend class X; // OK:X 被当做模板名
  9. X* q; // OK:X 被当做类型名,等价于 X<T1, T2>
  10. };

在类模板特化或部分特化的作用域内,当将注入类名用作类型名时,它等价于模板名后随环绕于 <> 中的该类模板特化或部分特化的各个模板实参。

  1. template<>
  2. struct X<void, void> {
  3. X* p; // OK:X 被当做类型名,等价于 X<void, void>
  4. template<class, class>
  5. friend class X; // OK:X 被当做模板名(与在模板中相同)
  6. X<void, void>* q; // OK:X 被当做模板名
  7. };
  8. template<class T>
  9. struct X<char, T> {
  10. X* p, q; // OK:X 被当做类型名,等价于 X<char, T>
  11. using r = X<int, int>; // OK:可用它指名另一特化
  12. };

只要在作用域中,类模板或类模板特化的注入类名就能被用作模板名或类型名之一。

  1. template<> class X<int, char> {
  2. class B {
  3. X a; // 表示 X<int, char>
  4. template<class,class> friend class X; // 表示 ::X
  5. };
  6. };
  7. template <class T> struct Base {
  8. Base* p;
  9. };
  10. template <class T> struct Derived: public Base<T> {
  11. typename Derived::Base* p; // 表示 Derived::Base<T>
  12. };
  13. template<class T, template<class> class U = T::template Base> struct Third { };
  14. Third<Derived<int>> t; // OK:默认实参将注入类名用作模板

找到注入类名的查找,在某些情况下可导致歧义(例如当在多于一个基类中找到它时)。若所有找到的注入类名都指代同一类模板的特化,且若该名字被用作模板名,则注入类名指代类模板自身而非其特化,且无歧义。

  1. template <class T> struct Base {};
  2. template <class T> struct Derived: Base<int>, Base<char> {
  3. typename Derived::Base b; // 错误:歧义
  4. typename Derived::Base<double> d; // OK
  5. };

注入类名与构造函数

构造函数没有名字,但在构造函数的声明与定义中,外围类的注入类名被认为是构造函数的名称。

在有限定的名字 C::D 中,若

  • 名字查找不忽略函数名,且
  • D 在类 C 的作用域中的查找找到了注入类名

则始终认为该限定名指名 C 的构造函数。这种名字只能用于构造函数的声明中(例如,在友元构造函数声明,构造函数模板特化,构造函数模板实例化,或构造函数定义中),或用于继承构造函数 (C++11 起)。

  1. struct A {
  2. A();
  3. A(int);
  4. template<class T> A(T) {}
  5. };
  6. using A_alias = A;
  7.  
  8. A::A() {}
  9. A_alias::A(int) {}
  10. template A::A(double);
  11.  
  12. struct B : A {
  13. using A_alias::A;
  14. };
  15.  
  16. A::A a; // 错误:A::A 被认为指名构造函数,而非类型
  17. struct A::A a2; // OK:与 'A a2;' 相同
  18. B::A b; // OK:与 'A b;' 相同