C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
注入类名
注入类名是在类的作用域内该类自身的名字。
类模板中,注入类名能用作指代当前模板的模板名,或指代当前实例化的类名。
解释
在类作用域中,当前类的名字被当做它如同是一个公开成员名一样;这被称为注入类名(injected-class-name)。该名字的声明点紧跟类定义的开花括号之后。
int X; struct X { void f() { X* p; // OK:X 指代注入类名 ::X* q; // 错误:名称查找找到变量名,它隐藏 struct 名 } };
与其他成员类似,注入类名可被继承。在私有或受保护继承的场合,可能导致某个间接基类的注入类名在派生类中最后变得不可访问。
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
中,若
- 名字查找不忽略函数名,且
- 对
D
在类C
的作用域中的查找找到了注入类名
则始终认为该限定名指名 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;' 相同