Injected-class-name
注入类名是指在该类作用域内使用的类本身的非限定名称。
在 类模板 中,注入类名既可用作引用当前模板的模板名称,也可用作引用当前实例化的类名称。
目录 |
说明
在 类作用域 中,当前类的类名或当前类模板的模板名被视为如同是公开成员名称;这被称为 注入类名 。该名称的声明点紧跟在类(模板)定义的开花括号之后。
int X; struct X { void f() { X* p; // 正确:X 是注入类名 ::X* q; // 错误:名称查找找到变量名,该名称隐藏了结构体名称 } }; template<class T> struct Y { void g() { Y* p; // 正确:Y 是注入类名 Y<T>* q; // 正确:Y 是注入类名,但 Y<T> 不是 } };
与其他成员类似,注入类名会被继承。当存在私有或受保护继承时,间接基类的注入类名可能在派生类中变得不可访问。
struct A {}; struct B : private A {}; struct C : public B { A* p; // 错误:注入类名 A 不可访问 ::A* q; // 正确,不使用注入类名 };
在类模板中
类模板的注入类名可用作模板名或类型名。
在以下情况下,被注入的类名会被视为类模板自身的模板名称:
否则,它将被视为类型名称,并等价于模板名称后随类模板的模板参数(置于
<>
中)。
template<template<class, class> class> struct A; template<class T1, class T2> struct X { X<T1, T2>* p; // 正确:X 被视为模板名称 using a = A<X>; // 正确:X 被视为模板名称 template<class U1, class U2> friend class X; // 正确:X 被视为模板名称 X* q; // 正确:X 被视为类型名称,等价于 X<T1, T2> };
在类
模板特化
或
偏特化
的作用域内,当注入类名被用作类型名时,它等同于后接类模板特化或偏特化的模板实参(用
<>
括起)的模板名称。
template<> struct X<void, void> { X* p; // 正确:X 被视为类型名称,等价于 X<void, void> template<class, class> friend class X; // 正确:X 被视为模板名称(与主模板中相同) X<void, void>* q; // 正确:X 被视为模板名称 }; template<class T> struct X<char, T> { X* p, q; // 正确:X 被视为类型名称,等价于 X<char, T> using r = X<int, int>; // 正确:可用于命名其他特化 };
类模板或类模板特化的注入类名在其作用域内既可用作模板名,也可用作类型名。
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; // 正确:Base 表示 Base<T> }; template<class T> struct Derived : public Base<T*> { typename Derived::Base* p; // 正确:Derived::Base 表示 Derived<T>::Base, // 即 Base<T*> }; template<class T, template<class> class U = T::template Base> struct Third {}; Third<Derived<int>> t; // 正确:默认参数使用注入类名作为模板
在某些情况下,查找注入类名的操作可能导致歧义(例如,如果在多个基类中都找到了该名称)。如果所有找到的注入类名都指向同一类模板的特化,且该名称被用作模板名时,该引用指向的是类模板本身而非其特化,此时不会产生歧义。
template<class T> struct Base {}; template<class T> struct Derived: Base<int>, Base<char> { typename Derived::Base b; // 错误:存在歧义 typename Derived::Base<double> d; // 正确 };
注入类名与构造函数
构造函数没有名称,但在构造函数声明和定义中,其外围类的注入类名被视为构造函数的名称。
在限定名称
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; // 正确,等同于 'A a2;' B::A b; // 正确,等同于 'A b;'
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 应用于 | 发布时行为 | 正确行为 |
|---|---|---|---|
| CWG 1004 | C++98 |
注入类名不能作为
模板模板参数使用 |
允许,此时它指向类
模板本身 |
| CWG 2637 | C++98 | 整个模板特化标识可以是注入类名 | 仅模板名称可以 |