class | 用法
| class Foo; // 类的前置声明 class Bar { // 类的定义 public: Bar(int i) : m_i(i) {} private: int m_i; }; template <class T> // 模板实参 void qux() { T t; } int main() { Bar Bar(1); class Bar Bar2(2); // 详述的类型 } |
this | 语法
关键词 this 是一个纯右值表达式,其值是隐式对象形参(在其上调用非静态成员函数的对象)的地址。它能出现于下列语境: 1) 在任何非静态成员函数体内,包括成员初始化器列表 2) 在非静态成员函数的声明中,(可选的)cv 限定符序列之后的任何位置,包括动态异常说明(弃用)、noexcept 说明(C++11)以及尾随返回类型(C++11 起) 3) 在默认成员初始化器中 (C++11 起) | |
virtual | 用法 virtual 说明符指定非静态成员函数为虚函数并支持动态调用派发。它只能在非静态成员函数的首个声明(即当它于类定义中声明时)的 声明说明符序列 中出现。 于每个指定为 virtual 的不同基类,最终派生对象中仅含有该类型的一个基类子对象,即使该类在继承层级中出现多次也是如此(只要它每次都以 virtual 继承)。
| |
explicit | 用法
explicit | (1) | | explicit ( 表达式 ) | (2) | (C++20 起) |
1) 指定构造函数或转换函数 (C++11 起)或推导指引 (C++17 起)为显式,即它不能用于隐式转换和复制初始化。
2) explicit 说明符可以与常量表达式一同使用。当且仅当该常量表达式求值为 true 时函数为显式。 | (C++20 起) |
explicit 说明符只可出现于在类定义之内的构造函数或转换函数 (C++11 起)的 声明说明符序列 中。 | struct A { A(int) { } // 转换构造函数 A(int, int) { } // 转换构造函数 (C++11) operator bool() const { return true; } }; struct B { explicit B(int) { } explicit B(int, int) { } explicit operator bool() const { return true; } }; int main() { A a1 = 1; // OK:复制初始化选择 A::A(int) A a2(2); // OK:直接初始化选择 A::A(int) A a3 {4, 5}; // OK:直接列表初始化选择 A::A(int, int) A a4 = {4, 5}; // OK:复制列表初始化选择 A::A(int, int) A a5 = (A)1; // OK:显式转型进行 static_cast if (a1) ; // OK:A::operator bool() bool na1 = a1; // OK:复制初始化选择 A::operator bool() bool na2 = static_cast<bool>(a1); // OK:static_cast 进行直接初始化 // B b1 = 1; // 错误:复制初始化不考虑 B::B(int) B b2(2); // OK:直接初始化选择 B::B(int) B b3 {4, 5}; // OK:直接列表初始化选择 B::B(int, int) // B b4 = {4, 5}; // 错误:复制列表初始化不考虑 B::B(int,int) B b5 = (B)1; // OK:显式转型进行 static_cast if (b2) ; // OK:B::operator bool() // bool nb1 = b2; // 错误:复制初始化不考虑 B::operator bool() bool nb2 = static_cast<bool>(b2); // OK:static_cast 进行直接初始化 } |
friend 说明 友元声明出现于类体内,并向一个函数或另一个类授予对包含友元声明的类的私有及受保护成员的访问权。 | 语法
friend 函数声明 | (1) | | friend 函数定义 | (2) | | friend 详述类说明符 ; | (3) | | friend 简单类型说明符 ; friend typename-说明符 ; | (4) | (C++11 起) |
| |
inline 说明: inline 关键词的本意是作为给优化器的指示器,以指示优先采用函数的内联替换而非进行函数调用,即并不执行将控制转移到函数体内的函数调用 CPU 指令,而是代之以执行函数体的一份副本而无需生成调用。这会避免函数调用的开销(传递实参及返回结果),但它可能导致更大的可执行文件,因为函数体必须被复制多次。 因为关键词 inline 的含义是非强制的,编译器拥有对任何未标记为 inline 的函数使用内联替换的自由,和对任何标记为 inline 的函数生成函数调用的自由。这些优化选择不改变上述关于多个定义和共享静态变量的规则。 限制:
- inline 说明符不能用于块作用域内(函数内部)的函数或变量 (C++17 起)声明
- inline 说明符不能重声明在翻译单元中已定义为非内联的函数或变量 (C++17 起)。
| 用法
声明有 constexpr 的函数是隐式的内联函数。 弃置的函数是隐式的内联函数:其(弃置)定义可出现在多于一个翻译单元中。 | (C++11 起) | 完全在 class/struct/union 的定义之内定义的函数,无论它是成员函数还是非成员 friend 函数,均为隐式的内联函数。 | |
inline 说明符,在用于具有静态存储期的变量(静态类成员或命名空间作用域变量)的 声明说明符序列 时,将变量声明为内联变量。 声明为 constexpr 的静态成员变量(但不是命名空间作用域变量)是隐式的内联变量。 |
inline namespace 命名空间名 { 声明序列 } / namespace 命名空间名::inline(C++20 起)(可选) 名字 { 声明序列 } 见:“C++声明 命名空间"篇
| |
template 用法
- 在模板定义中,template 可用于将某个待决名声明为模板。
- 声明模板
| 见模板篇 | |
export 用法
用于标记模板定义为被导出,这允许在其他翻译单元中声明但不定义同一模板。(1) | (C++11 前) | 不使用并保留该关键词。 | (C++11 起) (C++20 前) | 标记一个声明、一组声明或另一模块为当前模块所导出。 | (C++20 起) |
| export 是可选的修饰符,模板被导出(用于声明类模板时,它也声明其所有成员被导出)。对被导出模板进行实例化的文件不需要包含其定义:声明即已充分。 export module name
- 导出模块
- export template < 形参列表 > 类声明
| (1) export template <typename T> class MyClass { public: void memfun1(); // 被导出的函数 void memfun2(){ ... } // 隐式内联不能被导出 ... void memfun3(); // 显式内联不能被导出 ... }; template <typename T> inline void MyClass<T>::memfun3() // 使用inline关键字,显式内联 { ... } (2) // helloworld.cpp export module helloworld; // 模块声明 import <iostream>; // import声明 export void hello() { // export声明 std::cout << "Hello world!\n"; } // main.cpp import helloworld; // import声明 int main() { hello(); } |
extern 用法
|
extern 字符串字面量 { 声明序列(可选) } | (1) | extern 字符串字面量 声明 | (2) |
1) 将语言说明 字符串字面量 应用到声明于 声明序列 中的所有函数类型,具有外部链接的函数名,和具有外部链接的变量。 2) 将语言说明 字符串字面量 应用到单一声明或定义。
字符串字面量 | - | 所要求的语言链接的名字 | 声明序列 | - | 声明的序列,可以包含嵌套的链接说明 | 声明 | - | 一个声明 |
extern template class|struct 模板名 < 实参列表 > ; (C++11 起)
- 显示模板实例化声明
- "C++",默认的语言链接。
- "C",使得以 C 程序语言编写的函数进行链接,以及在 C++ 程序中定义能从 C 模块调用的函数成为可能。
- 外部链接声明
- 提供以不同程序语言编写的模块间的链接。
| (1) (2) extern "C" { int open(const char *pathname, int flags); // C 函数声明 } int main() { int fd = open("test.txt", 0); // 从 C++ 程序调用 C 函数 } // 此 C++ 函数能从 C 代码调用 extern "C" void handler(int) { std::cout << "Callback invoked\n"; // 它能使用 C++ } |
concept(C++20 起) 约束与概念: (1) 类模板,函数模板,以及非模板函数(常为类模板的成员),可以与约束(constraint)关联,它指定对模板实参的一些要求,这些要求可被用于选择最恰当的函数重载和模板特化。 (2) 这种要求的具名集合被称为概念(concept)。每个概念都是谓词,于编译时求值,并成为以之作为一项约束的模板接口的一部分: | | |
const 说明: | 用法
| |
static | 用法
| |
volatile | 用法
| |
static_assert 头文件: #include <type_traits> | 语法
static_assert ( 布尔常量表达式 , 消息 ) | (C++11 起) | static_assert ( 布尔常量表达式 ) | (C++17 起) |
解释 static_assert 声明可以出现在命名空间和块作用域中(作为块声明),也可以在类体中(作为成员声明)。 若 布尔常量表达式 返回 true,则此声明无效果。否则发布编译时错误,而若存在 消息,则诊断消息中包含其文本。 注解 因为 消息 必须是字符串字面量,所以它不能容纳动态信息,乃至自身并非字符串字面量的常量表达式。特别是它不能容纳模板类型实参的名字。 | #include <type_traits> template <class T> struct data_structure { static_assert(std::is_default_constructible<T>::value, "Data Structure requires default-constructible elements"); }; int main() { data_structure<int> ds_ok; } |
sizeof | 用法 语法
sizeof( 类型 ) | (1) | sizeof 表达式 | (2) |
两个版本都是 std::size_t 类型的常量表达式。 查询形参包中的元素数量。 语法
sizeof...( 形参包 ) | (C++11 起) |
返回 std::size_t 类型的常量。
| #include <iostream> #include <array> #include <type_traits> template<typename... Ts> constexpr auto make_array(Ts&&... ts) -> std::array<std::common_type_t<Ts...>,sizeof...(ts)> { return { std::forward<Ts>(ts)... }; } int main() { auto b = make_array(1, 2, 3); std::cout << b.size() << '\n'; for (auto i : b) std::cout << i << ' '; } |
mutable 许在即便包含它的对象被声明为 const 时仍可修改声明为 mutable 的类成员。 |
- 可出现于非引用非 const 类型的非静态数据成员的声明中:
- 从按复制捕获的形参中移除的 const 限定性的 lambda 声明符 (C++11 起)
| |
consteval(c++20) 说明:
- consteval - 指定函数是立即函数(immediate function),即每次调用该函数必须产生编译时常量。
| | consteval int sqr(int n) { return n*n; } constexpr int r = sqr(100); // OK int x = 100; int r2 = sqr(x); // 错误:调用不产生常量 |
constexpr(c++11) 说明: constexpr 说明符声明 可以 在编译时求 得 函数 或变量的 值。 然后这些 变量和函数(若给定了合适的函数实参)即可用于仅允许编译时常量表达式之处。 | constexpr 变量必须满足下列要求: constexpr 函数必须满足下列要求:
| (C++14 前) |
(=default; 或 =delete; 的函数体不含任何上述内容。)
- 非字面类型的变量定义
- 静态或线程存储期变量的定义
- 函数体必须不含:
- 拥有除 case 和 default 之外的标号的语句
| (C++14 起) |
函数体非 =delete; 的 constexpr 构造函数必须满足下列额外要求:
析构函数不能为 constexpr ,但能在常量表达式中调用平凡析构函数。 | (C++20 前) | 函数体非 =delete; 的 constexpr 析构函数必须满足下列额外要求:
- 每个用于销毁非静态数据成员与基类的析构函数必须为 constexpr 析构函数。
| (C++20 起) |
对于 constexpr 函数模板和类模板的 constexpr 函数成员,必须至少有一个特化满足上述要求。其他特化仍被认为是 constexpr,尽管常量表达式中不能出现这种函数的调用。 带初始化器的 if 语句
| (1) constexpr int factorial(int n) { return n <= 1? 1 : (n * factorial(n - 1)); } (2) template <typename T> auto get_value(T t) { if constexpr (std::is_pointer_v<T>) return *t; // 对 T = int* 推导返回类型为 int else return t; // 对 T = int 推导返回类型为 int } |
constinit(c++20) 说明: 说明符声明拥有静态或线程存储期的变量。 constinit 不能和 constexpr 或 consteval 一同使用。声明的变量为引用时, constinit 等价于 constexpr 。声明的变量为对象时, constexpr 强制对象必须拥有静态初始化和常量析构,并使对象有 const 限定,然而 constinit 不强制常量析构和 const 限定。 | | (1)初始化声明 const char *g() { return "dynamic initialization"; } constexpr const char *f(bool p) { return p ? "constant initializer" : g(); } constinit const char *c = f(true); // OK // constinit const char *d = f(false); // 错误 (2)用于非初始化声明,以告知编译器 thread_local 变量已被初始化。 extern thread_local constinit int x; int f() { return x; } // 无需检查防卫变量 |
const_cast 说明: 在有不同 cv 限定的类型间转换。 语法
const_cast < 新类型 > ( 表达式 ) |
返回 新类型 类型的值。 | 解释 (1) const_cast只能改变运算对象的底层const,也就是说: •常量指针转化为非常量的指针,并且仍然指向原来的对象 •常量引用转化为非常量的引用,并且仍然指向原来的对象 (2) 不能用const_cast改变表达式的类型 const char* cp; const_cast<string>(cp); //错误:const_cast不能改变表达式类型,只能改变常量属性 (3)使用const_cast把一个原本const的变量转换为非const是一个非定义行为(未定义行为指的是这个语句在标准C++中没有的规定,由编译器来决定如何处理) | (1) const int value=12; int new_value=const_cast<int>(value); //错误:const_cast只能改变运算对象的底层const,而对顶层const无能为力 const int* value_ptr=&value; int *ptr=const_cast<int*>(value_ptr);//正确:将常量指针转化为非常量指针,并仍然指向原来的对象 const int& value_re=value; int& re=const_cast<int&>(value_re);//正确:将常量引用转化为非常量引用,并仍然指向原来的对象 (3) struct type { int i; type(): i(3) {} void f(int v) const { // this->i = v; // 编译错误:this 是指向 const 的指针 const_cast<type*>(this)->i = v; // 只要该对象不是 const 就 OK } }; |
dynammic_cast 说明: 沿继承层级向上、向下及侧向,安全地转换到其他类的指针和引用。它支持运行时识别指针或引用。 语法
dynamic_cast < 新类型 > ( 表达式 ) |
新类型 | - | 指向完整类类型的指针、到完整类类型的引用,或指向(可选的 cv 限定)void 的指针 | 表达式 | - | 若 新类型 为引用,则为完整类类型的左值 (C++11 前)泛左值 (C++11 起)表达式,若 新类型 为指针,则为指向完整类类型的指针纯右值。 |
若转型成功,则 dynamic_cast 返回 新类型 类型的值。若转型失败且 新类型 是指针类型,则它返回该类型的空指针。若转型失败且 新类型 是引用类型,则它抛出与类型 std::bad_cast 的处理块匹配的异常。 | 解释 唯有下列转换能用 dynamic_cast 进行,但若这种转换会转换走常量性或易变性则亦不允许。 1) 若 表达式 的类型恰是 新类型 或 新类型 的较少 cv 限定版本,则结果是 表达式 具有 新类型 类型的值。(换言之,dynamic_cast 可用以添加常量性。隐式转换和 static_cast 亦能进行此转换。) 2) 若 表达式 的值是空指针值,则结果是 新类型 类型的空指针值。 3) 若 新类型 是到 Base 的指针或引用,且 表达式 的类型是到 Derived 的指针或引用,其中 Base 是 Derived 的唯一可访问基类,则结果是到 表达式 所标识的对象中 Base 类子对象的指针或引用。(注意:隐式转换和 static_cast 亦能进行此转换。) 4) 若 表达式 是指向多态类型的指针,且 新类型 是到 void 的指针,则结果是指向 表达式 所指向或引用的最终派生对象的指针。 5) 若 表达式 是到多态类型 Base 的指针或引用,且 新类型 是到 Derived 类型的指针或引用,则进行运行时检查: a) 检验 表达式 所指向/标识的最终派生对象。若在该对象中 表达式 指向/指代 Derived 的公开基类,且只有一个 Derived 类型对象从 表达式 所指向/标识的子对象派生,则转型结果指向/指代该 Derived 对象。(此之谓“向下转型(downcast)”。) b) 否则,若 表达式 指向/指代最终派生对象的公开基类,而同时最终派生对象拥有 Derived 类型的无歧义公开基类,则转型结果指向/指代该 Derived(此之谓“侧向转型(sidecast)”。) c) 否则,运行时检查失败。若 dynamic_cast 用于指针,则返回 新类型 类型的空指针值。若它用于引用,则抛出 std::bad_cast 异常。 6) 当在构造函数或析构函数中(直接或间接)使用 dynamic_cast,且 表达式 指代正在构造/销毁的对象时,该对象被认为是最终派生对象。若 新类型 不是到构造函数/析构函数自身的类或其基类之一的指针或引用,则行为未定义。 与其他转型表达式相似: 注解
- 亦可用 static_cast 进行向下转型,它避免运行时检查的开销,但只有在程序(通过某些其他逻辑)能够保证 表达式 所指向的对象肯定是 Derived 时才是安全的。
- 若 新类型 是左值引用类型(表达式 必为左值),则其结果为左值
- 若 新类型 是右值引用类型(表达式 为完整类类型,可以是左值或右值 (C++17 前)必为泛左值(纯右值被实质化) (C++17 起)),则其结果为亡值
- 若 新类型 是指针类型,则其结果为纯右值
| struct V { virtual void f() {}; // 必须为多态以使用运行时检查的 dynamic_cast }; struct A : virtual V {}; struct B : virtual V { B(V* v, A* a) { // 构造中转型(见后述 D 的构造函数中的调用) dynamic_cast<B*>(v); // 良好定义:v 有类型 V*,B 的 V 基类,产生 B* dynamic_cast<B*>(a); // 未定义行为:a 有类型 A*,A 非 B 的基类 } }; struct D : A, B { D() : B((A*)this, this) { } }; struct Base { virtual ~Base() {} }; struct Derived: Base { virtual void name() {} }; int main() { D d; // 最终派生对象 A& a = d; // 向上转型,可以用 dynamic_cast,但不必须 D& new_d = dynamic_cast<D&>(a); // 向下转型 B& new_b = dynamic_cast<B&>(a); // 侧向转型 Base* b1 = new Base; if(Derived* d = dynamic_cast<Derived*>(b1)) { std::cout << "downcast from b1 to d successful\n"; d->name(); // 可以安全调用 } Base* b2 = new Derived; if(Derived* d = dynamic_cast<Derived*>(b2)) { std::cout << "downcast from b2 to d successful\n"; d->name(); // 可以安全调用 } delete b1; delete b2; } |
reinterpret_cast 说明: 通过重新解释底层位模式在类型间转换。 reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。但若转换会转型走常量性或易变性则亦不允许。 语法
reinterpret_cast < 新类型 > ( 表达式 ) |
| 解释 与 static_cast 不同,但与 const_cast 类似,reinterpret_cast 表达式不会编译成任何 CPU 指令(除非在整数和指针间转换,或在指针表示依赖其类型的不明架构上)。它纯粹是一个编译时指令,指示编译器将 表达式 视为如同具有 新类型 类型一样处理。 reinterpret_cast用在任意指针(或引用)类型之间的转换;以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。
- 从指针类型到一个足够大的整数类型
- 从整数类型或者枚举类型到指针类型
- 从一个指向函数的指针到另一个不同类型的指向函数的指针
- 从一个指向对象的指针到另一个不同类型的指向对象的指针
- 从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
- 从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针
| int f() { return 42; } int main() { int i = 7; // 指针到整数并转回 std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast 为错误 std::cout << "The value of &i is 0x" << std::hex << v1 << '\n'; int* p1 = reinterpret_cast<int*>(v1); assert(p1 == &i); // 到另一函数指针并转回 void(*fp1)() = reinterpret_cast<void(*)()>(f); // fp1(); 未定义行为 int(*fp2)() = reinterpret_cast<int(*)()>(fp1); std::cout << std::dec << fp2() << '\n'; // 安全 // 通过指针的类型别名化 char* p2 = reinterpret_cast<char*>(&i); if(p2[0] == '\x7') std::cout << "This system is little-endian\n"; else std::cout << "This system is big-endian\n"; // 通过引用的类型别名化 reinterpret_cast<unsigned int&>(i) = 42; std::cout << i << '\n'; [[maybe_unused]] const int &const_iref = i; // int &iref = reinterpret_cast<int&>(const_iref); // 编译错误——不能去除 const // 必须用 const_cast 代替:int &iref = const_cast<int&>(const_iref); } |
static_cast 说明: 用隐式和用户定义转换的组合在类型间转换。static_cast相当于传统的C语言里的强制转换(相关类型转换) static_cast不能转换掉expression的const、volatile、或者__unaligned属性 语法
static_cast < 新类型 > ( 表达式 ) |
| 解释 唯有下列转换能用 static_cast 执行,但若转换会转型走常量性或易变性则亦不允许。 ①用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的; 进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。 ②用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。 ③把空指针转换成目标类型的空指针。 ④把任何类型的表达式转换成void类型。 | 基本类型: char a = 'a'; int b = static_cast<char>(a);//正确,将char型数据转换成int型数据 double *c = new double; void *d = static_cast<void*>(c);//正确,将double指针转换成void指针 int e = 10; const int f = static_cast<const int>(e);//正确,将int型数据转换成const int型数据 const int g = 20; int *h = static_cast<int*>(&g);//编译错误,static_cast不能转换掉g的const属性 |
interpret_case与static_cast对比 | 简而言之,static_cast<> 将尝试转换,举例来说,如float-到-integer,而reinterpret_cast<>简单改变编译器的意图重新考虑那个对象作为另一类型。 | float f = 12.3; float* pf = &f; // static cast<> // 成功编译, n = 12 int n = static_cast<int>(f); // 错误,指向的类型是无关的(译注:即指针变量pf是float类型,现在要被转换为int类型) //int* pn = static_cast<int*>(pf); //成功编译 void* pv = static_cast<void*>(pf); //成功编译, 但是 *pn2是无意义的内存(rubbish) int* pn2 = static_cast<int*>(pv);// reinterpret_cast<> //错误,编译器知道你应该调用static_cast<> //int i = reinterpret_cast<int>(f); //成功编译, 但是 *pn 实际上是无意义的内存,和 *pn2一样 int* pi = reinterpret_cast<int*>(pf); |
noexcept 头文件: #include <utility> | 用法 noexcept 运算符进行编译时检查,若表达式声明为不抛出任何异常则返回 true。 它可用于函数模板的 noexcept 说明符中,以声明函数将对某些类型抛出异常,但不对其他类型抛出。 语法
返回 bool 类型的纯右值。 noexcept 运算符不对 表达式 求值。 若 表达式 的潜在异常集合为空 (C++17 前)表达式 为不抛出 (C++17 起) 则结果为 true,否则结果为 false。 指定函数是否抛出异常。 语法
noexcept | (1) | | noexcept(表达式) | (2) | | throw() | (3) | (弃用)(C++20 中移除) |
1) 与 noexcept ( true ) 相同 2) 若 表达式 求值为 true,则声明函数为不抛出任何异常。
| void may_throw(); void no_throw() noexcept; auto lmay_throw = []{}; auto lno_throw = []() noexcept {}; class T{ public: ~T(){} // 析构函数妨碍了移动构造函数 // 复制构造函数为 noexcept }; >>> noexcept(may_throw()) //false noexcept(no_throw()) //true noexcept(lmay_throw()) //false noexcept(lno_throw()) //true noexcept(std::declval<T>().~T()) //true noexcept(T(std::declval<T>())) //true noexcept(T(t)) //true // foo 是否声明为 noexcept 取决于 T() 是否抛任何异常 template <class T> void foo() noexcept(noexcept(T())) {} void bar() noexcept(true) {} void baz() noexcept { throw 42; } // noexcept 等同于 noexcept(true) int main() { foo<int>(); // noexcept(noexcept(int())) => noexcept(true),故这是可以的 bar(); // 可以 baz(); // 能编译,但在运行时会调用 std::terminate }
|
alignas(c++11) 头文件:<stdalign.h> 与 <cstdalign> 说明:指定类型或对象的对齐要求。说明符可应用于变量或非位域类数据成员的 声明,或可应用于 class/struct/union 或枚举的定义。它不能应用于函数形参 或 catch 子句的异常形参。同一声明上,弱于其他 alignas 的有效的非零对齐被忽略。始终忽略 alignas(0)。 | alignas( 表达式 ):必须是求值为零或合法的对齐或扩展对齐的整型常量表达式。 alignas( 类型标识 ):等价于 alignas(alignof(类型)) alignas( 包 ... ):等价于对同一说明应用多个 alignas 说明符,逐个对应于形参包的各个成员, 形参包可以是类型或非类型形参包。 | // sse_t 类型的每个对象将对齐到 16 字节边界 struct alignas(16) sse_t { float sse_data[4]; }; // 数组 "cacheline" 将对齐到 128字节边界 alignas(128) char cacheline[128]; |
alignof 运算符(c++11) 头文件: 说明:查询类型的对齐要求。该类型可以为完整对象类型、元素类型完整的数组类型或者到这些类型之一 的引用类型。若类型为引用类型,则运算符返回被引用类型的对齐;若类型为数组类型, 则返回元素类型的对齐要求。 | alignof( 类型标识 ):返回std::size_t类型的值 | #include <iostream> struct Foo { int i; float f; char c; }; struct Empty {}; struct alignas(64) Empty64 {}; int main() { std::cout << "Alignment of" "\n" "- char : " << alignof(char) << "\n" "- pointer : " << alignof(int*) << "\n" "- class Foo : " << alignof(Foo) << "\n" "- empty class : " << alignof(Empty) << "\n" "- alignas(64) Empty: " << alignof(Empty64) << "\n"; } |
std::max_align_t(c++11) 头文件:<cstddef> 说明:std::max_align_t 通常是最大标量类型的同意词,在大多数平台上是 long double ,而其对齐要求是 8 或 16 。 | | #include <iostream> #include <cstddef> int main() { std::cout << alignof(std::max_align_t) << '\n'; } >> 16 |
and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq <% %> <: :> %: %:%: | && &= & | ~ ! != || |= ^ ^= { } [ ] # ## | if(n > 0 and n < 5) value and_eq data; |
asm 说明:给予在 C++ 程序中嵌入汇编语言源代码的能力。 | asm ( 字符串字面量 ) : 字符串字面量 通常是以汇编语言编写的短程序,每当执行这条声明时对其执行。 | |
atomic_cancel (TM TS) atomic_commit (TM TS) atomic_noexcept (TM TS) 说明: | 事务性内存(transactional memory):是在事务中结合语句组的并发同步机制,事务具有: 原子性(atomic)(要么语句全部发生,要么全部不发生) 隔离性(isolated)(事务中的语句不会观察到另一事务写入一半,即使它们并行执行) 同步块synchronized 复合语句 (1)如同在一个全局锁下执行复合语句:程序中的所有最外层同步块都以一个单独的全序执行。 在该顺序中,每个同步块的结尾同步于(synchronize with)下个同步块的开始。内嵌于其 他同步块的同步块没有特殊语义。同步块不是事务(不同于后面的原子块),并可以调用事务不安全的函数。 (2)以任何方式(抵达结尾,执行 goto、break、continue 或 return,或抛出异常)离开同步块都会退出该块, 而若所退出的块是外层块,则这在单一全序中同步于下个同步块。 不允许用 goto 或 switch 进入同步块。 原子块: atomic_noexcept 复合语句 atomic_cancel 复合语句 atomic_commit 复合语句 1) 若抛出异常,则调用 std::abort 2) 若抛出异常,则调用 std::abort,除非该异常是用于事务取消的异常之一(见后述),这种情况下事务被取消(cancel):程序中所有由该原子块的各操作的副作用所修改的内存位置的值,被还原到该原子块的执行开始时它们曾拥有的值,而异常照常持续栈回溯。 3) 若抛出异常,则正常地提交事务。 用于 atomic_cancel 块中的事务取消的异常有 std::bad_alloc、std::bad_array_new_length、std::bad_cast、std::bad_typeid、std::bad_exception、std::exception 和所有从它派生的标准库异常,以及特殊异常类型 std::tx_exception<T>。 不允许原子块中的 复合语句 执行任何非 transaction_safe 的表达式或语句,或调用非 transaction_safe 的函数(这是编译时错误)。 (4)以除异常之外的任何方式(抵达结尾、goto、break、continue、return)离开原子块时,将提交事务。若用 std::longjmp 退出原子块则行为未定义。 事务安全的函数: 可在函数声明中用关键词 transaction_safe 将其显式声明为事务安全。 | (1) #include <iostream> #include <vector> #include <thread> int f() { static int i = 0; synchronized { // 开始同步块 std::cout << i << " -> "; ++i; // 每次调用 f() 都获得 i 的唯一值 std::cout << i << '\n'; return i; // 结束同步块 } } int main() { std::vector<std::thread> v(10); for(auto& t: v) t = std::thread([]{ for(int n = 0; n < 10; ++n) f(); }); for(auto& t: v) t.join(); } >>> 0 -> 1 1 -> 2 2 -> 3 ... 99 -> 100 (2) // 每次调用 f() 都取得 i 的唯一值,即使以并行进行 int f() { static int i = 0; atomic_noexcept { // 开始事务 // printf("before %d\n", i); // 错误:不能调用非事务安全的函数 ++i; return i; // 提交事务 } } |
auto (1)类型推导C++11 起: 对于变量,指定要从其初始化器自动推导出其类型。 对于函数,指定要从其 return 语句推导出其返回类型。(C++14 起) 对于非类型模板形参,指定要从实参推导出其类型。(C++17 起) (2):带尾随返回类型的函数声明: 尾随返回类型仅在最外层函数声明符中允许使用。此情况下的 声明说明符序列 必须包含关键词 auto (3)结构化绑定声明 见:结构化绑定声明(c++17) | (1) auto (1) (C++11 起) decltype(auto) (2) (C++14 起) 类型制约 auto (3) (C++20 起) 类型制约 decltype(auto) (4) (C++20 起) (2) auto 说明符亦可用于后随尾随返回类型的函数声明符,该情况下返回类型为其尾随返回类型(它也可以是占位符类型): auto (*p)() -> int; // 声明指向返回 int 的函数的指针 尾随返回类型,当返回类型取决于实参名时,例如 template <class T, class U> auto add(T t, U u) -> decltype(t + u);,或当返回类型复杂时,例如在 auto fpif(int)->int(*)(int) 中,尾随返回类型很有用 若函数声明的 声明说明符序列 包含关键词 auto,则尾随返回类型可以省略,而编译器将从 return 语句中所用的表达式的类型推导出它。若返回类型使用的不是 decltype(auto),则推导遵循模板实参推导的规则进行。 | (1) template<class T, class U> auto add(T t, U u) { return t + u; } // 返回类型是 operator+(T, U) 的类型 (2) // 在其所调用的函数返回引用的情况下 // 函数调用的完美转发必须用 decltype(auto) template<class F, class... Args> decltype(auto) PerfectForward(F fun, Args&&... args) { return fun(std::forward<Args>(args)...); } (3) template<auto n> // C++17 auto 形参声明 auto f() -> std::pair<decltype(n), decltype(n)> // auto 不能从花括号初始化器列表推导 { return {n, n}; } |
bool/break/switch/case/continue/switch/do/if/enum/false/for/goto/while/void/return/requires | 常规操作 | |
char/char8_t(c++20)/char16_t(c++20)/char32_t(c++20)/double/float/int/long/short/ wchar_t/unsigned/signed/register | | |
default | 用法 类名() = default ;
| |
delete | ::(可选) delete 表达式 ::(可选) delete [] 表达式 如果取代函数体而使用特殊语法 = delete ;,则该函数被定义为弃置的(deleted)。任何弃置函数的使用都是非良构的(程序无法编译)。 类名() = delete ;
| |
new | 用法 语法
::(可选) new (布置参数)(可选) ( 类型 ) 初始化器(可选) | (1) | ::(可选) new (布置参数)(可选) 类型 初始化器(可选) | (2) |
1) 尝试创建类型标识 类型 所指代的类型的一个对象,它可以是数组类型,可以包含占位符类型说明符 (C++11 起),或包含将由类模板实参推导推出的类模板名 (C++17 起)。 2) 同上,但 类型 不能包含括号: operator new, operator new[]
- 分配函数:作为运算符式的函数名
- new 表达式
- 创建并初始化拥有动态存储期的对象,这些对象的生存期不受它们创建时所在的作用域限制。
- new 表达式尝试申请存储空间,并在已申请的存储空间上,尝试构造并初始化为一个无名对象,或无名对象的数组。new表达式返回一个指向所构造的对象或者对象数组的纯右值指针。
| |
operator 说明: 为用户定义类型的操作数定制 C++ 运算符。 | 语法 重载的运算符是具有特殊的函数名的函数:
operator op | (1) | | operator 类型 | (2) | | operator new operator new [] | (3) | | operator delete operator delete [] | (4) | | operator "" 后缀标识符 | (5) | (C++11 起) | operator co_await | (6) | (C++20 起) |
op | - | 下列 38 (C++20 前)39 (C++20 起) 个运算符之一: + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= <=> (C++20 起) && || ++ -- , ->* -> ( ) [ ] |
| |
try-catch: 说明:将一或多个异常处理块(catch 子句)与复合语句关联。 | 语法
try 复合语句 处理块序列 其中 处理块序列 是一或多个 处理块 的序列,它有下列语法: |
catch ( attr(可选) 类型说明符序列 声明符 ) 复合语句 | (1) | catch ( attr(可选) 类型说明符序列 抽象声明符(可选) ) 复合语句 | (2) | catch ( ... ) 复合语句 | (3) |
| |
throw 用法
| 见 异常 篇 | |
thread_local 用法
|
- 这类对象的存储在线程开始时分配,并在线程结束时解分配。每个线程拥有其自身的对象实例。只有声明为 thread_local 的对象拥有此存储期。
- 关键词仅允许搭配声明于命名空间作用域的对象、声明于块作用域的对象及静态数据成员。它指示对象具有线程存储期。它能与 static 或 extern 结合,以分别指定内部或外部连接(但静态数据成员始终拥有外部链接),但额外的 static 不影响存储期。(C++11 起)
| thread_local unsigned int rage = 1; |
协程: 协程是能暂停执行以在之后恢复的函数。协程是无栈的:它们通过返回到调用方暂停执行,并且从栈分离存储恢复所要求的数据。这允许编写异步执行的顺序代码(例如不使用显式的回调来处理非阻塞 I/O),还支持对惰性计算的无限序列上的算法及其他用途。 若函数的定义做下列任何内容之一,则它是协 co_wait(c++20)
co_yield(c++20)
co_return(c++20)
- 用关键词 co_return 完成执行并返回一个值
| | |
decltype(c++11) 说明:在难以或不可能以标准写法进行声明的类型时,decltype 很有用,例如 lambda 相关类型或依赖于模板形参的类型。 | 语法
decltype ( 实体 ) | (1) | (C++11 起) | decltype ( 表达式 ) | (2) | (C++11 起) |
(1) 若实参为无括号的标识表达式或无括号的类成员访问表达式,则 decltype 产生以此表达式命名的实体的类型。若无这种实体,或该实参指名某个重载函数,则程序非良构。 若实参是指名某个结构化绑定的无括号的标识表达式,则 decltype 产生被引用类型(在关于结构化绑定声明的说明中有所描述)。(C++17 起) 若实参是指名某个非类型模板形参的无括号的标识表达式,则 decltype 生成该模板形参的类型(当该模板形参以占位符类型声明时,则为进行任何所需的类型推导后的类型)。(C++20 起) (2) 若实参是其他类型为 T 的任何表达式,且 a) 若 表达式 的值类别为亡值,则 decltype 产生 T&&; b) 若 表达式 的值类别为左值,则 decltype 产生 T&; c) 若 表达式 的值类别为纯右值,则 decltype 产生 T。 注意如果对象的名字带有括号,则它被当做通常的左值表达式,从而 decltype(x) 和 decltype((x)) 通常是不同的类型。
decltype(auto) (C++14 起) | | | 类型制约 decltype(auto) (C++20 起) | | |
- 占位类型说明符
- 检查实体的声明类型,或表达式的类型和值类别。
| #include <iostream> struct A { double x; }; const A* a; decltype(a->x) y; // y 的类型是 double(其声明类型) decltype((a->x)) z = y; // z 的类型是 const double&(左值表达式) template<typename T, typename U> auto add(T t, U u) -> decltype(t + u) // 返回类型依赖于模板形参 { // C++14 开始可以推导返回类型 return t+u; } |
typeid 头文件:<typeinfo> 说明: 用于必须知晓多态对象的动态类型的场合以及静态类型鉴别。 语法
typeid ( 类型 ) | (1) | typeid ( 表达式 ) | (2) |
typeid 表达式为左值表达式,指代一个具有静态存储期的,多态类型 std::type_info 或某个其派生类型的 const 限定版本类型的对象。 | 解释 1) 指代一个表示 类型 类型的 std::type_info 对象。若 类型 为引用类型,则结果所指代的 std::type_info 对象表示被引用的类型。 2) 检验表达式 表达式 a) 若 表达式 为标识某个多态类型(即声明或继承至少一个虚函数的类)对象的泛左值表达式,则 typeid 表达式对该表达式求值,然后指代表示该表达式动态类型的 std::type_info 对象。若该泛左值表达式为通过对一个指针应用一元 * 运算符所得,且该指针为空指针值,则抛出 std::bad_typeid 类型或从 std::bad_typeid 派生的类型的异常。 b) 若 表达式 不是多态类型的泛左值表达式,则 typeid 不对该表达式求值,而它所指代的 std::type_info 对象表示该表达式的静态类型。不进行左值到右值、数组到指针或函数到指针转换。然而对于纯右值参数,(形式上)要进行临时量实质化:参数必须在 typeid 表达式所出现的语境中可析构。 (C++17 起)在所有情况下,typeid 都忽略顶层的 cv 限定符(即 typeid(T) == typeid(const T))。 若 typeid 的操作数为类类型或到类类型的引用,则该类类型必须不是不完整类型。 若对处于构造和销毁过程中的对象(在构造函数或析构函数之内,包括构造函数的初始化器列表或默认成员初始化器)使用 typeid,则此 typeid 所指代的 std::type_info 对象表示正在构造或销毁的类,即便它不是最终派生类。 | const std::type_info& ti1 = typeid(A); const std::type_info& ti2 = typeid(A); assert(&ti1 == &ti2); // 不保证 assert(ti1.hash_code() == ti2.hash_code()); // 保证 assert(std::type_index(ti1) == std::type_index(ti2)); // 保证 |
struct 用法
- 若存在于作用域中的某个函数或变量所拥有的名字,与某个非联合体类类型的名字相同,则可在其名字之前加上 struct 来消歧义,这产生一个详述类型说明符
- 复合类型的声明
| | |
union 用法
- 若存在于作用域中的某个函数或变量所拥有的名字与某个联合体类型的名字相同,则可在该名字之前加上 union 以消歧义,这产生详述类型说明符
- 联合体类型的声明
| 联合体是特殊的类类型,它在一个时刻只能保有其一个非静态数据成员。 联合体声明的类说明符与类或结构体的声明相似: union attr 类头名 { 成员说明 } union { 成员说明 } ; C++ 标准库包含 std::variant,它可取代联合体和联合体式的类的大多数用途
| #include <variant> #include <iostream> int main() { std::variant<char, int, double> s = 'a'; std::visit([](auto x){ std::cout << x << '\n';}, s); s = 123; std::visit([](auto x){ std::cout << x << '\n';}, s); } |
enum 语法: enum-关键词 attr(可选) enum-名(可选) enum-基(可选)(C++11) { 枚举项列表(可选) } (1) enum-关键词 attr(可选) enum-名 enum-基(可选) ; (2)(C++11 起) |
enum-关键字 | : | enum、enum class(C++11 起) 或 enum struct(C++11 起) 之一 | attr(C++11) | - | 任意数量的属性的可选序列 | enum-名 | - | 所声明的枚举的名字。若存在,且若此声明为重声明,则其之前可带有 嵌套名说明符(C++11 起),即名字和作用域解析运算符 :: 的序列并以作用域解析运算符结尾。仅可在无作用域枚举声明中省略名字 | enum-基(C++11) | - | 冒号 (:),后随指名某个整型类型的 类型说明符序列(若它为 cv 限定,则忽略其限定性),该类型将作为此枚举类型的固定底层类型 | 枚举项列表 | - | 枚举项定义的逗号分隔列表,每项要么是简单的 标识符,它成为枚举项之名,要么是带初始化器的标识符:标识符 = 常量表达式。 |
有两种截然不同的枚举:无作用域枚举(以 enum-关键词 enum 声明)和有作用域枚举(以 enum-关键词 enum class 或 enum struct 声明)。
enum 名字 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } | (1) | | enum 名字 : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } | (2) | (C++11 起) | enum 名字 : 类型 ; | (3) | (C++11 起) |
1) 声明无作用域枚举类型,其底层类型不固定(此情况中,底层类型是由实现定义的某个能表示所有枚举项值的整型类型;此类型不大于 int,除非枚举项的值不能放入 int 或 unsigned int。若 枚举项列表 为空,则底层类型为如同枚举拥有单个值为 0 的枚举项)。 2) 声明底层类型固定的无作用域枚举类型。 3) 无作用域枚举的不可见枚举声明必须指定底层类型。 每个 枚举项 都成为该枚举类型(即 名字)的一个具名常量,在其外围作用域可见,且可用于要求常量的任何位置。整数、浮点和枚举类型的值,可用 static_cast 或显式转型转换到任何枚举类型。 无作用域枚举的 名字 可以忽略:这种声明仅将各枚举项引入到其外围作用域中:
enum struct|class 名字 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } | (1) | enum struct|class 名字 : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } | (2) | enum struct|class 名字 ; | (3) | enum struct|class 名字 : 类型 ; | (4) |
1) 声明底层类型为 int 的有作用域枚举类型(关键词 class 与 struct 完全等价) 2) 声明底层类型为 类型 的有作用域枚举类型 3) 底层类型为 int 的有作用域枚举类型的不可见枚举声明 4) 底层类型为 类型 的有作用域枚举类型的不可见枚举声明 每个 枚举项 都成为该枚举的类型(即 名字)的具名常量,它为该枚举的作用域所包含,且可用作用域解析运算符访问。没有从有作用域枚举项到整数类型的隐式转换,尽管 static_cast 可用于获得枚举项的数值。
using enum 嵌套名说明符(可选) 名字 ; | (C++20 起) |
using enum 声明引入其所指名的枚举的枚举项名字,如同用对每个枚举项的 using 声明。在类作用域中时, using enum 声明将其所指名的枚举的枚举项名字作为成员添加到作用域,使成员查找能访问它们 | (1)无作用域枚举 enum access_t { read = 1, write = 2, exec = 4 }; //枚举项:1、2、4 范围:0..7 access_t rwe = static_cast<access_t>(7); assert((rwe & read) && (rwe & write) && (rwe & exec)); access_t x = static_cast<access_t>(8.0); // C++17 起为未定义行为 access_t y = static_cast<access_t>(8); // C++17 起为未定义行为 enum foo { a = 0, b = UINT_MAX }; // 范围:[0, UINT_MAX] foo x= foo(-1); // C++17 起为未定义行为,即使 foo 的底层类型为 unsigned int (2)有作用域枚举 enum class Color { red, green = 20, blue }; Color r = Color::blue; switch(r) { case Color::red : std::cout << "red\n"; break; case Color::green: std::cout << "green\n"; break; case Color::blue : std::cout << "blue\n"; break; } // int n = r; // 错误:不存在从有作用域枚举到 int 的转换 int n = static_cast<int>(r); // OK, n = 21 enum byte : unsigned char {}; // byte 是新的整数类型 byte b { 42 }; // C++17 起 OK(直接列表初始化) (3)using enum 声明 enum class fruit { orange, apple }; struct S { using enum fruit; // OK :引入 orange 与 apple 到 S 中 }; void f() { S s; s.orange; // OK :指名 fruit::orange S::orange; // OK :指名 fruit::orange } |
typedef typedef - 创建能在任何位置替代(可能复杂的)类型名的别名。
| | |
using | 用法 见 命名空间 篇 将别处定义的名字引入到此 using 声明所出现的声明区中。
using typename(可选) 嵌套名说明符 无限定标识 ; | (C++17 前) | using 声明符列表 ; | (C++17 起) |
typename | - | 在 using 声明从基类向类模板中引入成员类型时,关键词 typename 是用于解决待决名所必须的 |
解释 using 声明可用于将命名空间成员引入到另一命名空间与块作用域,或将基类成员引入到派生类定义中,或将枚举项引入命名空间、块或类作用域中
有多于一个 using 声明符的 using 声明,等价于有单个 using 声明符的 using 声明的对应序列。 | (C++17 起) |
在命名空间和块作用域中: using 声明将另一命名空间的成员引入到当前命名空间或块作用域中-->using std::string; 在类定义中 using 声明将基类成员引入到派生类的定义中,例如将基类的受保护成员暴露为派生类的公开成员。此情况下 嵌套名说明符 必须指名所定义的类的某个基类。若其名字是该基类的某个重载的成员函数的名字,则具有该名字的所有基类成员函数均被引入。若派生类已有具有相同名字、形参列表和限定的成员,则派生类成员隐藏或覆盖从基类引入的成员(不与之冲突)。 继承构造函数 若 using 声明指代正在定义的类的某个直接基类的构造函数(例如 using Base::Base;),则在初始化派生类时,令该基类的所有构造函数(忽略成员访问)均对重载决议可见。 引入有作用域枚举项 using 声明亦能将枚举的枚举项引入命名空间、块和类作用域。using 声明亦能用于无作用域枚举项。
using 标识符 attr(可选) = 类型标识 ; | (1) | template < 模板形参列表 > using 标识符 attr(可选) = 类型标识 ; | (2) |
解释 1) 类型别名声明引入一个名字,可用做 类型标识 所指代的类型的同意词。它不引入新类型,且不能更改既存类型名的含义。类型别名声明和 typedef 声明之间无区别。此声明可出现于块作用域、类作用域或命名空间作用域。 2) 别名模板是一种模板,当其特化时等价于以别名模板的模板实参来替换 类型标识 中的模板形参的结果。
| 在类定义中 #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() } 继承构造函数 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 无默认构造函数 } 引入有作用域枚举项 enum class button { up, down }; struct S { using button::up; button b = up; // OK }; |
typename 用法
- 在模板声明中,typename 可用作 class 的代替品,以声明类型模板形参和模板形参 (C++17 起)。
- 在模板的声明或定义内,typename 可用于声明某个待决的有限定名是类型。
- 在模板的声明或定义内, (C++11 前)typename 可在非待决的有限定类型名之前使用。此情况下它没有效果。
- 在类型要求的要求中。(C++20 起)
| | |
namespace 见以下:c++声明中 namespace 说明 | | |