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++ 关键词
using 声明
将别处定义的名字引入到此 using 声明所出现的声明区中。
using typename (可选) 嵌套名说明符 无限定标识 ;
|
(C++17 前) | ||||||||
using 声明符列表 ;
|
(C++17 起) | ||||||||
嵌套名说明符 | - | 名字与作用域解析运算符 :: 的序列,以作用域解析运算符结尾。单个 :: 指代全局命名空间。
|
无限定标识 | - | 标识表达式 |
typename | - | 在 using 声明从基类向类模板中引入成员类型时,关键词 typename 是用于解决待决名所必须的
|
声明符列表 | - | 一或多个形式为 typename (可选) 嵌套名说明符 无限定标识 的声明符的逗号分隔列表。某些或全部的声明符可后随省略号 ... 以指示包展开
|
解释
using 声明可用于将命名空间成员引入到另一命名空间与块作用域,或将基类成员引入到派生类定义中。
有多于一个 using 声明符的 using 声明,等价于有单个 using 声明符的 using 声明的对应序列。 |
(C++17 起) |
在命名空间和块作用域中
using 声明将另一命名空间的成员引入到当前命名空间或块作用域中
#include <iostream> #include <string> using std::string; int main() { string str = "Example"; using std::cout; cout << str; }
细节见命名空间。
在类定义中
using 声明将基类成员引入到派生类的定义中,例如将基类的受保护成员暴露为派生类的公开成员。此情况下 嵌套名说明符 必须指名所定义的类的某个基类。若其名字是该基类的某个重载的成员函数的名字,则具有该名字的所有基类成员函数均被引入。若派生类已有具有相同名字、形参列表和限定的成员,则派生类成员隐藏或覆盖从基类引入的成员(不与之冲突)。
#include <iostream> struct B { virtual void f(int) { std::cout << "B::f\n"; } void g(char) { std::cout << "B::g\n"; } void h(int) { std::cout << "B::h\n"; } protected: int m; // B::m 为受保护 typedef int value_type; }; struct D : B { using B::m; // D::m 为公开 using B::value_type; // D::value_type 为公开 using B::f; void f(int) { std::cout << "D::f\n"; } // D::f(int) 覆盖 B::f(int) using B::g; void g(int) { std::cout << "D::g\n"; } // g(int) 与 g(char) 均作为 D 成员可见 using B::h; void h(int) { std::cout << "D::h\n"; } // D::h(int) 隐藏 B::h(int) }; int main() { D d; B& b = d; // b.m = 2; // 错误,B::m 受保护 d.m = 1; // 受保护的 B::m 可作为公开的 D::m 访问 b.f(1); // 调用派生类 f() d.f(1); // 调用派生类 f() d.g(1); // 调用派生类 g(int) d.g('a'); // 调用基类 g(char) b.h(1); // 调用基类 h() d.h(1); // 调用派生类 h() }
输出:
D::f D::f D::g B::g B::h D::h
继承构造函数若 using 声明指代正在定义的类的某个直接基类的构造函数(例如 using Base::Base;),则在初始化派生类时,令该基类的所有构造函数(忽略成员访问)均对重载决议可见。 若重载决议选择了继承的构造函数,则当它被用于构造相应基类的对象时可访问,它是可访问的:引入它的 using 声明的可访问性被忽略。 若在初始化这种派生类对象时重载决议选择了继承的构造函数之一,则用这个继承的构造函数,对从之继承该构造函数的 struct B1 { B1(int, ...) { } }; struct B2 { B2(double) { } }; int get(); struct D1 : B1 { using B1::B1; // 继承 B1(int, ...) int x; int y = get(); }; void test() { D1 d(2, 3, 4); // OK:B1 通过调用 B1(2, 3, 4) 初始化, // 然后 d.x 被默认初始化(不进行初始化), // 然后 d.y 通过调用 get() 初始化 D1 e; // 错误:D1 无默认构造函数 } struct D2 : B2 { using B2::B2; // 继承 B2(double) B1 b; }; D2 f(1.0); // 错误:B1 无默认构造函数 struct W { W(int); }; struct X : virtual W { using W::W; // 继承 W(int) X() = delete; }; struct Y : X { using X::X; }; struct Z : Y, virtual W { using Y::Y; }; Z z(0); // OK:Y 的初始化不调用 X 的默认构造函数 若构造函数从 B 类型的多个基类子对象继承,则程序非良构,这与多继承的非静态成员函数相似: struct A { A(int); }; struct B : A { using A::A; }; struct C1 : B { using B::B; }; struct C2 : B { using B::B; }; struct D1 : C1, C2 { using C1::C1; using C2::C2; }; D1 d1(0); // 非良构:从不同的 B 基类子对象继承的构造函数 struct V1 : virtual B { using B::B; }; struct V2 : virtual B { using B::B; }; struct D2 : V1, V2 { using V1::V1; using V2::V2; }; D2 d2(0); // OK:只有一个 B 子对象。 // 这初始化虚 B 基类,它初始化 A 基类 // 然后如同用预置的默认构造函数 // 初始化 V1 与 V2 基类 与任何其他非静态成员函数的 using 声明相同,若继承的构造函数的签名与 struct B1 { B1(int); }; struct B2 { B2(int); }; struct D2 : B1, B2 { using B1::B1; using B2::B2; D2(int); // OK:D2::D2(int) 隐藏 B1::B1(int) 和 B2::B2(int) }; D2 d2(0); // 调用 D2::D2(int) |
(C++11 起) |
注解
只有在 using 声明中明确提及的名字才被传送到声明区中:特别是,using 声明枚举类型名时,并不传送其枚举项。
using 声明不能指代命名空间,有作用域枚举项,基类的析构函数,或用户定义转换函数的成员模板的特化。
using 声明不能指名成员模板的特化(文法不容许 模板标识):
struct B { template<class T> void f(); }; struct D : B { using B::f; // OK:指名模板 // using B::f<int>; // 错误:指名模板特化 void g() { f<int>(); } };
using 声明亦无法用于将待决的成员模板的名字作为模板名引入(不容许待决名的 template
消歧义符):
template<class X> struct B { template<class T> void f(T); }; template<class Y> struct D : B<Y> { // using B<Y>::template f; // 错误:不允许消歧义符 using B<Y>::f; // 能编译,但 f 不是模板名 void g() { // f<int>(0); // 错误:已知 f 不是目标名, // 故 < 不开始模板实参列表 f(0); // OK } };
若 using 声明将基类的赋值运算符引入到派生类,而其签名恰好与派生类的复制赋值或移动赋值运算符匹配,则该运算符被派生类的隐式声明的复制/移动赋值运算符所隐藏。相同的规则适用于继承恰好与派生类的移动/复制构造函数匹配的基类构造函数的 using 声明。 (C++11 起)
一项针对 C++11 的缺陷报告追溯地更改了继承构造函数的语义。以前,继承构造函数的声明导致将一组合成的构造函数声明注入到派生类中,这导致冗余的实参复制/移动,并与某些形式的 SFINAE 的交互有问题,而且某些情况下在主流 ABI 上无法实现。旧编译器可能仍然实现先前的语义。
|
(C++11 起) |
using 声明中的包展开,使得不利用递归而组成暴露变长基类的重载成员的类成为可能: template <typename... Ts> struct Overloader : Ts... { using Ts::operator()...; // 从每个基类暴露 operator() }; template <typename... T> Overloader(T...) -> Overloader<T...>; // C++17 推导指引 int main() { auto o = Overloader{ [] (auto const& a) {std::cout << a;}, [] (float f) {std::cout << std::setprecision(3) << f;} }; } |
(C++17 起) |
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
DR | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
P0136R1 | C++11 | 继承构造函数声明会将额外的构造函数注入派生类 | 导致名字查找能找到基类构造函数 |