注入类名
来自 cppreference.cn
注入类名是在其自身作用域内的类的非限定名称。
在 类模板 中,注入类名可以用作引用当前模板的模板名称,或用作引用当前实例化的类名称。
目录 |
[编辑] 解释
在 类作用域 中,当前类的类名或当前类模板的模板名称被视为如同它是公共成员名称;这被称为注入类名。名称的声明点紧随类(模板)定义的开括号之后。
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 } };
与其他成员一样,注入类名是可继承的。在存在私有或保护继承的情况下,间接基类的注入类名可能最终在派生类中变得不可访问。
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 };
[编辑] 在类模板中
类模板的注入类名可以用作模板名称或类型名称。
在以下情况下,注入类名被视为类模板本身的模板名称
否则,它被视为类型名称,并且等同于模板名称后跟类模板的模板形参,并用 <>
括起来。
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
的构造函数。这样的名称只能用于构造函数的声明中(例如,在友元构造函数声明、构造函数模板特化、构造函数模板实例化或构造函数定义中)或用于继承构造函数(自 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 可以是注入类名 | 只有模板名称可以 |