C++ 基础入门

C++ 基础入门

1. 基本概念

数据类型

所有的数据类型只是利用内存的手段

基本数据类型

整数、浮点、指针、结构体/类、联合体、枚举

整数

整数包括有符号signed和无符号unsigned两种属性

字符 char

短整数 short

整数 int

长整数 long

64位整数 long long

浮点

单精度 float

双精度 double

长双精度 long double

指针

空类型指针 void*

有类型指针 类型*

二级和多级指针 void** void***

结构体/类

结构体的定义 struct

类的定义 class

联合体

联合体的定义 union

联合体的作用 共用内存

枚举

枚举的定义 enum、enum class

枚举的作用 增加代码可读性、提供成组的整数

程序结构

顺序结构 按照语句先后执行的结构

分支结构 按照条件部分执行的结构

循环结构 按照条件重复执行的结构

程序入口

函数main的完整写法和简略写法

完整写法int main(int argc,char* argv[],char* env[]){}

简略写法 int main(){}

代码结构

预处理:包括头文件、编译参数、宏处理

源码文件:函数/类/模板的定义或实现

代码文件基本构成

对于可执行程序,至少要有一个入口函数

对于静态库,至少要有一个函数或者属性

对于动态库,至少要有一个导出函数或者属性

编译和运行

源码 一般是文本文件,提供程序的主体逻辑

中间文件 经过编译器,由源码生成的文件,可以生成其他文件

静态库文件 一般是中间文件按照固定格式的打包集合,无法直接执行

动态库文件 可执行,但是无法单独执行

可执行文件 可以独立在指定操作系统执行的文件

开发人员开发源码,然后交由编译器生成中间文件。

接着由链接器生成库文件或者执行文件

其中执行文件可以在操作系统中独立执行

动态库文件则需要有可执行文件加载才能执行

静态库文件无法执行,必须经由链接器变成动态库或者可执行文件,才能真正的执行

语句和语句块

语句的基本写法

以分号结尾的表达式

定义类型:

类型 变量名

定义符 类型名 定义模块

语句类型

函数调用

表达式

语句块

用大括号包含的定义、调用或者表达式

标识符

命名规则

下划线或字母开头后续可以是数字字符下划线

名称空间

名称空间的定义

名称空间也叫命名空间

namespace 名称{

}

可以嵌套,可以可以分散在多个文件中声明

名称空间的作用

区分同名的定义或者对象

名称空间的使用

名称::类型名称/对象名称

可以使用using namespace 名称;来简化内容

传统关键字

数据类型

关键字bool 布尔类型

关键字char 字符类型

关键字short 短整类型

关键字wchar_t 宽字符型

关键字int 整数类型

关键字long 长整类型

关键字double 双精度型

关键字float 单精度型

关键字signed 有符号型

关键字struct 结构体

关键字enum 枚举

关键字union 联合体

关键字const 常量

关键字auto 自动类型。类型会依据表达式进行推导

关键字void 无类型

关键字unsigned 无符号类型

常量关键字

关键字true 真

关键字false 假

面向对象

关键字class 类

关键字private 私有

关键字protected 保护

关键字public 公有

关键字inline 内联

关键字template 模板

关键字this 自指针

关键字typename 模板类型符

关键字virtual 虚函数

关键字friend 友元

流程控制

关键字break 中止;可以终止switch、for和while

关键字continue 继续;可以用于循环

关键字switch 分支

关键字case 分支条件

关键字default 默认处理

关键字do 循环

关键字for 循环

关键字while 循环

关键字goto 强制无条件跳转

关键字if 条件分支

关键字else 为假处理

关键字return 函数返回

异常处理

关键字try 尝试运行

关键字catch 捕获异常

关键字throw 抛出异常

运算符

关键字new 申请内存

关键字delete 释放内存

关键字sizeof 目标长度

关键字operator 运算符重载

关键字typeid 获取类型

显示类型转换

关键字const_cast 修改const与volatile之间的转换,对指针和引用都有效果

关键字dynamic_cast 动态类型转换 基类转派生类、基类的左值转换成派生类的引用

关键字static_cast 强制类型转换

关键字reinterpret_cast 指针、引用和算术类型 与 整数互转

按照转换能力排序

越后的转换能力越强,但是越危险

左值 可以赋值和修改

右值 不可以赋值和修改

左值引用

右值引用

其他

关键字asm 嵌入式汇编

关键字namespace 名称空间

关键字explicit 显示调用

关键字export 导出对象

关键字extern 外部对象

关键字static 静态对象

关键字typedef 类型定义

关键字using 声明使用名称空间;不同版本意义略有不同

关键字mutable 可变的(用于修改const修饰的函数)

关键字register 寄存器变量(基本已经废了)

关键字volatile 易变的类型

关键字__cdecl C调用约定

关键字__stdcall 标准调用约定

关键字__thiscall this调用约定

函数的声明、实现、调用以及递归调用

函数的声明及其作用

完整函数声明:

返回值 调用约定 函数名 参数列表 修饰符

调用约定:

__cdecl C调用约定,调用者清理栈

__stdcall 标准调用约定,被调用者自己清理栈,替换了__pascal

__fastcall 快速调用约定,第一第二个参数使用ecx edx传递,其他参数才入栈,被调用者自己清理栈

__thiscall 一般不直接指明,成员函数默认是这个调用约定,用ecx传递this指针,对于可变参数函数,this在最后入栈

naked 裸函数。declspec(naked)vc中的声明方式 gcc使用__attribute((naked))

裸函数遵循标准调用约定,但是函数内部完全按照用户操作来处理

修饰符:

const 这个函数不会直接或者间接修改任何成员变量

函数的实现及其限制

完整函数实现:

返回值 调用约定 函数名 参数列表 修饰符 函数实现语句块

不能有两个同名同参数的函数

返回值和调用约定无法区分函数,但是修饰符可以

函数的调用及其过程

参数入栈

函数调用

本地栈构造

函数执行

本地栈销毁

函数返回

函数的递归调用

递归是一种分而治之的思想体现

每一次递归必须要将问题的规模进行递减

否则就可能导致无穷循环

递归能解决的是规模能够快速递减的情况

否则运用递归就是一种灾难性的行为

VirtualProtect PAGE_READONLY PAGE_EXCUTE

指针、引用和函数指针

指针/引用以及底层实现

指针和引用在底层是等价的

区别仅在语法层面

引用也可能出现空指针问题

考虑到指针和引用底层是等价的

理论上是可以互换的

指针和引用的优缺点

从语法上讲指针可以中途更换指向的对象,引用不能

引用的优点就是从语法上来杜绝野指针、空指针的问题

缺点就是引用只能在初始化的时候赋值,其后无法更改

指针的优点就是可以灵活的指向各种对象

缺点就是容易产生野指针、空指针的问题

函数指针的定义

指向函数代码地址的指针,就是函数指针

因为大部分的函数代码都在代码段

所以函数指针往往都和代码段属性一样

可读可执行,但是不能写

函数指针的作用

异步操作中提供回调操作

分支数量过大的时候,提供数组分支

分支条件不为整数时,通过映射表来进行分支


2. 类与对象

类的定义:方法和属性的集合

是一种用户定义的类型

用户可以定义其初始化、销毁、运算符、方法和属性

其代码定义一般如下:

class 类名

{

访问修饰符:

数据类型 属性;

返回值 方法名 参数列表 修饰符 方法定义主体

返回值 方法名 参数列表 修饰符;

};

方法定义主体也可以额外去实现,定义的时候不用实现

实现的时候,格式如下:

返回值 类名::方法名 参数列表 修饰符 方法定义主体

类的方法也可以叫成员函数

对于类的静态属性,必须要进行实现

其在类中只能算声明,不能算实现

其实现定义格式如下:

数据类型 类名::属性 = 初始化值;

由于类是一个用户自定义类型

所以其结尾和结构体类似,都是以;结束

访问修饰符有public protected private

其中public表示公开类型,任何内部外部的对象都可以访问

而protected表示保护类型,仅内部的方法或者子类方法可以访问

最后private表示私有类型,仅自身方法可以访问,子类和外部对象都不可以访问

注意struct默认访问修饰符是public

而class默认访问修饰符是private

类的初始化方法被称为构造函数

类的销毁方法被称为析构函数

类从同类型的数据创建对象的方法称为拷贝构造函数(也叫复制构造函数)

这几个方法如果没有指定,系统会给出默认的构造和析构方法

注意,类的友元可以不受访问修饰符限制,访问任意属性和方法

友元分为友元类和友元函数

友元函数格式:friend 返回值类型 函数或者类成员函数 参数列表 修饰符;

友元类格式: friend class 类名;

友元类关系无法传递

比如A、B、C三个类

A是B的友元类,B是C的友元类,这并不意味着A是C的友元类

友元要谨慎授予,因为无限制的访问可能导致严重的耦合和未知的后果

类中所有的非静态方法都有一个this指针

这个指针指向自己

指针实际上是遵循__thiscall来传入值的

所以这个指针也有可能为空!!!

类中的静态方法和属性

静态属性 该类定义的所有对象和子类对象共用一个

所以有时候在设计中,认为它的作用范围是类的,而不是对象的

静态方法 注意静态方法不遵循__thiscall调用约定

所以这些方法中无法使用this指针

继承

使用一个类来定义另外一个类

提高代码重用效率

用来定义别的类的类,被称为基类;被定义的类,称为派生类/子类

运算符重载

不可重载运算符

函数重载(overload)

同一个类中,几个具有不同参数列表的同名函数

重载和函数返回值没有关系

l 相同的范围

l 函数名字相同

l 参数列表不同

l Virtual关键字可有可无

l 返回类型可同可不同

函数重写(override)

派生类中,与父类中带virtual函数同名、同参数列表、同返回值的函数

l 不在同一个作用域

l 函数名字相同

l 参数列表相同

l 基类必须有virtual关键字,不能有static

l 返回值相同

函数隐藏

l 不在同一个作用域

l 函数名字相同

l 返回类型可同可不同

l 参数不同/相同,基类的函数将被隐藏


3. 类的进阶概念

类与类的关系

继承与派生、实现 父子关系 没有了爹,也不会有儿子

组合 人和四肢的关系 缺了会严重影响功能

聚合 公司和员工的关系 缺了对整体功能会有重大影响

关联 班级和学生的关系 缺了有影响,但是问题不大

依赖 人和车的关系 没有车虽然有影响,但是人还是可以移动的

多态就是一个接口多种形态

C++调用成员函数的时候,会依据对象的类型来执行不同的函数

虚函数是使用了关键字virtual的函数

虚函数会根据调用的对象类型,来动态选择调用函数

本质上是通过一个函数表(虚函数表)来实现这种动态调用

纯虚函数

纯虚函数是用来统一派生类方法的方法

该方法实际上并没有实现

其实现是在派生类之中完成的

往往配合多态来完成实际功能

抽象类(接口)

接口是一插座,外部通过这个插座使用内部提供的服务

但是内部的具体实现,外部不用关心

内部发生代码重构的时候,只要外部接口没有变化

依赖此接口的任何外部对象、方法都无需修改

同样,只要接口不变,外部如何使用接口

内部也无需关心

通过接口层,将内部和外部代码进行解耦

多继承

一个派生类可以从多个父类继承方法和属性

多继承的常见形式:

class 派生类 : 访问修饰符 父类1,访问修饰符 父类2,...

{派生类具体定义}

多继承父类同名属性和方法的访问:

父类::属性/方法;

虚函数表详解:

虚函数表

一般继承(无虚函数覆盖和有虚函数覆盖)

虚函数表是一张一维表

多重继承(无虚函数覆盖和有虚函数覆盖)

虚函数表是一张二维表

问题:

违规通过父类指针访问子类虚函数

违规访问非公有虚函数

常量对象和常方法 const

常方法的修正 mutable


4. 泛型编程

泛型编程最初的目的是解耦数据类型和数据结构

结构是通用的,比如列表、数组、栈、映射表、集合、树、图等等

而结构中的数据是千变万化的

可以是整数列表、小数列表、对象列表、字符串列表等等

也可以是整数数组、小数数组、对象数组、字符串数组等等

其他的数据结构也是类似的

进一步的,可以将算法与数据类型解耦

排序算法关心的不再是算法中数据的类型

而是回归到算法本身,是排序本身

类似的查找算法、递归算法、回归算法等等都可以如此进行

泛型编程的代表作是STL(标准目标库Standard Template Library)

STL里面包含

泛型算法

泛型迭代器

泛型容器

函数对象

STL是一种软件架构

C++是泛型编程的摇篮

缺点:

由于抽象的类型,导致一旦产生编译错误的时候

其错误不是那么一目了然

排查泛型的编译错误

有时候让人绝望

过度的抽象,使得执行效率有所下降

一些效率敏感的场合

需要改用其他的方式

来解决问题


5. 智能指针

自动指针(auto_ptr) 没有引用计数、赋值会丢失所有权 C89 建议废弃!

创建 auto_ptr p(new T());

取值get 获取对象指针

置空reset 置空会导致内存释放

释放release 会导致智能指针变空,不过该函数会返回前面的指针值

=赋值 赋值会导致 赋值的指针 失去对原有对象的所有权,并被置空

可以使用*和->访问指针

使用auto_ptr_ref解决构造函数的问题

例如auto_ptrp(auto_ptr(new int(10)));

首先auto_ptr(new int(10)) 构造了一个auto_ptr

然后这个传给p的构造函数

但是问题来了

此时没有const auto_ptr(auto_ptr& const p);函数

所以此路不通

但是存在 operator auto_ptr_ref()

于是尝试把auto_ptr转为auto_ptr_ref

转换之后,又有一个构造函数可以接受这个参数

从而完成构造

我们可以实验一下

共享指针(shared_ptr)有引用计数、赋值会增加计数

创建 shared_ptr p(new T()); 或者shared_ptr p = make_shared();

可以自定义删除操作shared_ptr p(new T(),删除函数);

删除函数void deleter(T* p){/自定义处理/ }

也可以使用lambda表达式做删除函数

交换swap 可以互换shared_ptr值

重置reset 当前指针置空(也可以通过=nullptr来置空),会自动释放内存

可以使用*和-> 运算符

优点:

默认是空指针

可以通过 !p或者pNULL或者pnullptr 来判空

自动释放内存

缺点:无法使用++、--、[]运算符来访问

缺陷:

必须使用new出来的对象赋值;

不要用局部变量地址赋值;

不要用同一个指针给两个shared_ptr赋值

弱引用指针(weak_ptr)必须搭配shared_ptr使用,不会增加shared_ptr的引用计数

方法operator= 可以从weak和shared获取值

方法swap 可以互换weak值

方法reset 当前置空

方法use_count 当前shared_ptr的引用计数

方法expired 判断是否过期

方法lock获取对应的shared_ptr

唯一指针(unique_ptr)不能赋值,只能传递

构造 直接构造unique_ptr(new T()) make_unique()

也可以传递deleter类(和shared不一样,前面是传函数)

参考default_delete结构体

方法get

方法reset

方法release

方法swap

智能指针的优点与缺点

优点:

解决对象共享

解决对象不可迁移(比如套接字、线程)

解决循环引用

缺点:

不是线程安全的!不是线程安全的!不是线程安全的!

智能指针代码解读

自动指针

共享指针

唯一指针

弱引用指针


6. 常见的STL用法以及语法解读

//算法:

//排序sort和stable_sort。稳定排序,如果两个元素值一样,排序的时候不会交换其顺序

//查找search 

#include <algorithm>

#include <functional>

#include <iostream>

#include <string>

#include <vector>

 

template <typename Container>

bool in_quote(const Container& cont, const std::string& s) {

 return std::search(cont.begin(), cont.end(), s.begin(), s.end()) !=

​     cont.end();

}

 

int main() {

 std::string str = "why waste time learning, when ignorance is instantaneous?";

 // str.find() can be used as well

 std::cout << std::boolalpha << in_quote(str, "learning") << std::endl

​      << in_quote(str, "lemming") << std::endl;

 

 std::vector<char> vec(str.begin(), str.end());

 std::cout << std::boolalpha << in_quote(vec, "learning") << std::endl

​      << in_quote(vec, "lemming") << std::endl;

 return 0;

}

 

 

//替换replace

#include <algorithm>

template <class ForwardIterator, class T>void replace (ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value);

 

 

//合并merge

#include <iostream>

#include <iterator>

#include <algorithm>

#include <vector>

#include <random>

#include <functional>

 

int main()

{

  // 以随机数填充 vector

  std::random_device rd;

  std::mt19937 mt(rd());

  std::uniform_int_distribution<> dis(0, 9);

 

  std::vector<int> v1(10), v2(10);

  std::generate(v1.begin(), v1.end(), std::bind(dis, std::ref(mt)));

   std::generate(v2.begin(), v2.end(), std::bind(dis, std::ref(mt)));

 

  // 排序

  std::sort(v1.begin(), v1.end());

  std::sort(v2.begin(), v2.end());

 

  // 输出 v1

  std::cout << "v1 : ";

  std::copy(v1.begin(), v1.end(), std::ostream_iterator<int>(std::cout, " "));

  std::cout << '\n';

 

  // 输出 v2

  std::cout << "v2 : ";

  std::copy(v2.begin(), v2.end(), std::ostream_iterator<int>(std::cout, " "));

  std::cout << '\n';

 

  // 归并

  std::vector<int> dst;

  std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));

 

  // 输出

  std::cout << "dst: ";

  std::copy(dst.begin(), dst.end(), std::ostream_iterator<int>(std::cout, " "));

  std::cout << '\n';

}

迭代器:

各个容器都有各自的迭代器

容器:

向量(数组)vertor

映射表map

列表 list

栈stack

双端队列 deque

集合set

函数对象:

对函数调用运算符进行重载类的对象,也称为仿函数

像函数,但是比函数强大;是对象,但是往往没有一般对象那么复杂

Std::bind(函数,


7. C++11/14/17/20新特性

官方资料地址

https://en.cppreference.com/w/cpp/compiler_support

C++11新特性

关键字static_assert 静态断言

语法:static_assert(常量表达式,”提示字符串”)

例如:static_assert(sizeof(int)==2,”不支持16位代码编译”);

右尖括号 解决了>>符号的混淆

例如typedef std::vector<std::vector> Flags;在早期版本中会报错

扩展friend声明 可以声明一个声明的类、类的别名、或者未声明的类为友元

如果类未声明,要使其为友元,那么需要使用class或者struct来指明其类型

追加类型long long 支持64位整数,其常量使用LL后缀 例如1LL

函数printf使用%lld来支持输出

支持type traits 类型特征的判断

比如 is_void is_integral is_array is_enum

关键字auto意义改变

示例:int func(); auto& x=func(); const auto&x = func(); 这样都会报错

不能在临时变量上面使用引用

允许多个变量: int i;auto a=1,*b=&i;

禁止自己初始化自己 auto j=&j;

允许初始化 auto x(1);

允许new 比如 new auto(1.0)

允许 auto*x = new auto(1);

改变 auto不再是存储类型的一种了(老版里面它是一种存储类型,其他的还包括register、mutable、static、extern)

Delegating constructors 代理构造函数

允许一个构造函数使用另外一个构造进行构造

比如:

class Foo{

public:

Foo(char x,int y){}

Foo(int y):Foo(‘a’,y){}

}

extern template 外部模板 允许在不同的位置声明和使用模板

示例:extern template class vector

主要是避免同一种类型的模板被反复实现

连累编译器和链接器最后要去重,浪费大量时间

关键字constexpr 期待函数能够在编译器就得出返回值

是给模板用的

这个和const不同,const表示函数返回的类型是const属性的

简单点说 const更多的是只读的含义,而constexpr才是常量

Template aliases 模板别名

示例:

using Vec = vector<T,Alloc<T>>;

关键字char16_t、char32_t 允许16bit和32bit的字符,为多种编码而设置

UTF-8 UTF-16 UTF-32

关键字alignas 对齐 可以声明类/结构体/联合体/无属性类/变量(不能是形参)

示例:

// every object of type struct_float will be aligned to alignof(float) boundary// (usually 4)struct alignas(float) struct_float {

// your definition here

};

 // every object of type sse_t will be aligned to 256-byte boundary

struct alignas(256) sse_t{

float sse_data[4];

};

 // the array "cacheline" will be aligned to 128-byte boundary

alignas(128) char cacheline[128];

关键字alignof 获取一个类型的字节长度

Defaulted and deleted函数

允许使用默认函数或者删除函数

这样可以使得子类自由保留或者去除父类函数

Strongly-typed enum 强枚举类型 enum class

解决传统枚举的几个问题

1 隐式转换为整数

2 枚举类型的大小会依据实现出现变化

这样枚举类型的变量其size无法确定

3 同名枚举成员会产生重名错误

4 解决上面问题的适合,避免扩展不兼容问题

Atomic operations 追加原子操作库

关键字nullptr 空指针关键字,避免了NULL宏的问题

关键字explicit显示转换 要求实参和形参类型完全一致,不提供隐式转换

Ref-qualifiers 限定符

包括const volatile

也包括&&右值引用 &引用

字符串标识 支持更多的字符串类型

比如 u8”” u”” U”” L”” “”

Inline namespaces 内联名称空间

允许在名称空间内部声明和使用名称空间

继承构造函数

Trailing function return types

Unrestricted unions

Variadic templates 可变参数模板

template<typename... Types>

Tuple<Types...> make_tuple(Types&&... args)

{

return tuple<Types...>(args...);//圆组

}

表达式的SFINAE特性

全称Substitution Failure Is Not An Error 即 替换失败不是错误

比如这样一种情况

template<typename T>class is_class {

  typedef char yes[1];

  typedef char no [2];

  template<typename C> static yes& test(int C::*); // selected if C is a class type

  template<typename C> static no& test(...);   // selected otherwise

 public:

static bool const value = sizeof(test<T>(0)) == sizeof(yes);

};

可以通过value来判断类型T是类还是其他对象

本地和无名类型做为模板参数 允许本地类型或者无名类型做为模板参数

关键字thread_local

动态并发初始化和析构 线程存储和静态存储进行了区分

垃圾回收和可触达泄露检测 没有编译器支持!

初始化列表 允许使用初始化列表来初始化对象或者成员属性

非静态数据成员初始化 允许对非静态成员进行初始化

属性

其他属性修饰还包括

[[noreturn]]

[[carries_dependency]]

[[deprecated]]或者[[deprecated(“原因”)]]

[[fallthrough]]

[[nodiscard]]或者[[nodiscard(“原因”)]]

[[likely]][[unlikely]]

[[no_unique_address]]

[[optimize_for_synchronized]]

Forward(opaque)enum declarations

枚举类 enum class {枚举值1,枚举值2,...};

可以解决隐式转换为整数的情况

用户定义数据标识

允许用户自定义数据标识

比如:

#include <chrono>

#include <thread>

#include <iostream>

using namespace std::chrono_literals; //标准库的预定义文字量:用法:1000ms

using Ms = std::chrono::milliseconds; //类型别名

auto operator "" _ms(unsigned long long value) { return Ms(value); }  //自定义的文字量,用法: 1000_ms

int main() {

  std::this_thread::sleep_for( std::chrono::millisecond(1000) );

  std::this_thread::sleep_for( Ms(3000) );

  std::this_thread::sleep_for( 3000_ms );

  std::this_thread::sleep_for( 3000ms );

}

 

Rvalue引用 右值引用(不能赋值,可以用于完整转发)

Lambda表达式 (语法糖)

匿名函数对象

捕获列表修饰符 -> 返回值 {函数体}

捕获为空,表示不捕获任何值,表达式内外的变量不能互访

&,表示用引用的方式捕获所有变量,表达式可以读写外部变量

=,表示用值的方式捕获所有变量,表达式可以用只读的方式访问外部变量

this,表示捕获this指针,这样lambda就可以通过this访问成员

*this,表示以值的形式使用成员

变量名称,表示以值的形式捕获指定变量,表达式可以读取该变量值

&变量名称,表示以引用的形式捕获指定变量,表达式可以读写该变量值

捕获方式可以在不冲突的情况下任意组合

例如

[&,var1,var2]除了var1和var2以值捕获,其他变量以引用捕获

[=,this,&var]var以引用捕获,其他以值捕获,同时捕获this指针

多用于函数做为参数传递的情况

比如std的for_each

捕获的变量不能重复或者冲突

[i,i]、[this,*this]是不被允许的

Lambda可以嵌套

例如:

auto f(){

 return [&]{

 return [=]{

  return 10;

}

}

}

注意,lambda默认时const修饰的的函数,如果注明是mutable,才能修改本地变量

语法糖,可以解决一些回调函数的问题

Range-for loop for(类型 x:表达式)

例如: for(auto& i:v)//v=std::vector

类型前面可以有一些属性修饰符,比如[[maybe_unused]]

详情见属性一条

和标准库中的for_each类似

关键字noexcept 表明是否会抛出异常

noexcept 不会抛出异常 noexcept(false) 可能抛出异常

默认特殊成员函数处理 =default =delete throw

关键字override 成员函数后缀修饰符,覆盖父类的虚函数(覆盖的函数必须是virtual函数)

关键字final 类型和成员函数后缀修饰符

用在成员函数时,表示子类无法覆盖的方法

用在类型时,表示该类型不会有派生类型

关键字decltype

获取对象的类型并用来声明另外一个变量

decltype(x)表示x的类型

decltype((x))表示x的类型的引用类型


C++14新特性

调整某些C++上下文转换的建议

模板的类型转换需要按照建议来,算法和指针可以

直接转换成int*类型,不符合转换建议,不行

从i转换为int不行,但是转换为算法可以

整数标记,单引号做为整数分隔符

二进制 0b101010

八进制 052

十进制 42

十六进制 0x2a 0X2A

64位整数 18446744073709550592ull

或者18’446’744’073’709’550’592llu

或者 1844’6744’0737’0955’0592uLL

或者 184467’4407370’95505’92LLU

或者 18446744073709550592UZ

或者 18446744073709550592Z

正常函数的返回类型推断

示例:

auto sum(int i){

 if(i==1)return i;

 else return sum(i-1)+i;

}

 

模板变量

tempalte<typename T>

T val = T(3.1415926535897932385);

 

int main(int, char**)

{

  val<float> = 0.6180339887498948482;

  std::cout << val<float> << std::endl; // 使用新赋的值

  std::cout << val<double> << std::endl; // 使用默认值

  return 0;

}

Lambda表达式

追加了对模板的支持

对于constexpr的扩展 允许对成员函数和隐式常量进行处理

可以用来声明一个成员属性,该属性会做为常量来处理

比如 constexpr int bufsz=1024;//const int

也可以用来声明一个成员变量,会尽量在编译期间计算

比如constexpr int square(int x){return x*x;}

编译器会在调用编译的时候,尝试进行计算,然后得到结果

而不是在运行时计算

初始化数组,或者使用一个属性来初始化另外一个属性

比如

int a[]={1,2,3,4};

或者

Struct S {int a;char*b;int c=b[a];};

关键字[[deprecated]]

Gcc attribute((deprecated(“消息字符串”))) int a;

VS __declspec(deprecated(“消息字符串”)) int a;

废弃的

当编译的适合,会提示已弃用警告

消息字符串可以不提供,也可以提供

如果提供警告会显示消息字符串

delete[]表达式 后面的[]中是被认为是delete的

例如: delete ([]{return new int;})();可以

而delete []{return new int;}() 不可以

因为[]被认为是delete的附属,使得lambda结构被破坏


C++17新特性

auto 可以通过初始化列表来推导

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>

auto x2 = { 1, 2.0 }; //error:cannot deduce element type

auto x3{ 1,2};// error: not a single element

auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int>

auto x5{ 3 }; // decltype(x5) is int

 

允许没有消息的static_assert

格式:static_assert(常量表达式);

或者 static_assert(常量表达式,消息字符串);

关键字typename嵌套

例如

template struct A {};

template using B = int;

template<template class X> struct C;//这里的class表示类,而不是模板类型

C ca; // ok

C cb; // ok, not a class template

template<template typename X> struct D; // error, cannot use typename here

三字符组转换

![img](file:///C:/TMP/msohtmlclip1/01/clip_image026.jpg)

这个转换会发生在字符串和注释识别之前

名称空间可以嵌套定义

关键字inline可以修饰namespace

例如:inline namespace 名称空间1 { /空的/ }

using namespace 名称空间1;

namespace 名称空间1 { 追加的代码 }

相当于用来占位置的

字符标识

![img](file:///C:/TMP/msohtmlclip1/01/clip_image028.jpg)

新增了u8的标识

允许对非类型模板参数进行常量计算

template<int* p> class X { };

 

int a[10];

struct S { int m; static int s; } s;

 

X<&a[2]> x3; // error: address of array element

X<&s.m> x4; // error: address of non-static member

X<&s.s> x5; // 过去error: &S::s must be used =>现在OK: address of static member

X<&S::s> x6; // OK: address of static member

折叠表达

(pack op ...)

(... op pack)

(pack op ... op init)

(init op ... op pack)

运算符op可以是: + - * / % ^ & | = < > << >> += -= = /= %= ^= &= |= <<= >>= == != <= >= && || , . ->*

其中pack 是不包含低级运算符的未展开表达式

其中init 是不包含未展开的表达式,不包含有低级运算符的表达式

例如:

template<typename... Args>

bool all(Args... args) { return (... && args); }

 

bool b = all(true, true, true, false);

 // within all(), the unary left fold expands as

 // return ((true && true) && true) && false;

 // b is false

 

例如:

template<typename ...Args>

int sum(Args&&... args) {

//  return (args + ... + 1 * 2); // Error: operator with precedence below cast

  return (args + ... + (1 * 2)); // OK

}

 

例如:

#include <iostream>

#include <vector>

#include <climits>

#include <cstdint>

#include <type_traits>

#include <utility>

 

template<typename ...Args>

void printer(Args&&... args) {

  (std::cout << ... << args) << '\n';

}

 

template<typename T, typename... Args>

void push_back_vec(std::vector<T>& v, Args&&... args)

{

  static_assert((std::is_constructible_v<T, Args&&> && ...));

  (v.push_back(std::forward<Args>(args)), ...);

}

 

// compile-time endianness swap based on http://stackoverflow.com/a/36937049 

template<class T, std::size_t... N>

constexpr T bswap_impl(T i, std::index_sequence<N...>) {

 return (((i >> N*CHAR_BIT & std::uint8_t(-1)) << (sizeof(T)-1-N)*CHAR_BIT) | ...);

}

template<class T, class U = std::make_unsigned_t<T>>

constexpr U bswap(T i) {

 return bswap_impl<U>(i, std::make_index_sequence<sizeof(T)>{});

}

 

int main()

{

  printer(1, 2, 3, "abc");

 

  std::vector<int> v;

  push_back_vec(v, 6, 2, 45, 12);

  push_back_vec(v, 1, 2, 9);

  for (int i : v) std::cout << i << ' ';

 

  static_assert(bswap<std::uint16_t>(0x1234u)==0x3412u);

  static_assert(bswap<std::uint64_t>(0x0123456789abcdefULL)==0xefcdab8967452301ULL);

}

删除register关键字做为存储类的描述

存储类现在有static thread_local extern mutable

删除operator++(bool)

现在bool类型不会再有++运算了(C98引入的奇怪机制)

异常规范作为类型系统的一部分

void f() noexcept(true); 和 void f() noexcept(false); 是具有不同类型的函数。函数指针可以按符合常理的方式转换。(但是这两个函数 f 不能构成重载。)这个变化加强了类型系统。例如,API可以通过类型系统要求回调函数不抛出异常。

对聚合初始化(aggregate initialization)的扩展

注释:列表初始化(list initialization)现在可以用聚合初始化的方式初始化基类子对象(base subobject):

对于聚合类型

struct base { int a1, a2; };

struct derived : base { int b1; };

以下初始化现在是合法的:

derived{{1, 2}, 3}、derived{{}, 3}。

追加__has_include

注释:检查能否包含特定头文件的预处理运算符。

示例:

\

#if __has_include(<optional>)

# include <optional>

# define have_optional 1

#elif __has_include(<experimental/optional>)

# include <experimental/optional>

# define have_optional 1

# define experimental_optional 1

#else

# define have_optional 0

#endif

 

反向继承构造函数

struct B {

…

}

 

struct B1 {

 B1(int);

};

 

struct B2 {

 B2(int);

};

 

struct D1 : B1, B2 {

 using B1::B1;

 using B2::B2;

};

D1 d1(0);  // ill-formed: ambiguous

 

struct D2 : B1, B2 {

 using B1::B1;

 using B2::B2;

 D2(int);  // OK: D2::D2(int) hides B1::B1(int) and B2::B2(int)

};

D2 d2(0);  // calls D2::D2(int)

 

扩展lambda(可以捕获*this的值,但是只能为只读)

class Work {

private:

int value ;

public:

Work() : value(42) {}

std::future spawn()

{

return std::async( [=,tmp=*this]()->int{ return tmp.value ; });

}

};

struct S2 { void f(int i); };

void S2::f(int i) {

[&, i]{ }; // OK

[&, &i]{ }; // error: i preceded by & when & is the default

[=, *this]{ };  // OK

[=, this]{ }; // error: this when = is the default

[i, i]{ }; // error: i repeated

[this, *this]{ }; // error: this appears twice

}

固定枚举(fixed enum)值的构造

注释:类型为固定枚举 E 的变量可以用 E e { 5 }; 的形式定义,

不再需要更啰嗦的 E e { E(5) };。

基于范围的for循环(range-based for)接受不同的begin/end类型

注释:for (decl : expr) 的规则受到重写,从以前的auto __begin = begin-expr, __end = end-expr;改成现在使用的 auto __begin = begin-expr; auto __end = end-expr;。

这使得基于范围的for循环为新的Ranges(工作正在进行中)做好准备。

[[fallthrough]]

用法:

void f(int n)

{

​     void g(),h(),i();

​     switch(n){

​     case 1:

​     case 2:

​         g();

​         [[fallthrough]];//ok

​     case 3:

​         h();

​     case 4:

​         i();

​         [[fallthrough]];//ill-formed

​     }

}

 

[[nodiscard]]

示例:

struct [[nodiscard]] error_info { /*...*/ };

error_info enable_missile_safety_mode();

void launch_missiles();

void test_missiles() {

enable_missile_safety_mode(); // 鼓励警告

launch_missiles();

}

error_info &foo();

void f() { foo(); } // 引用类型,不鼓励使用警告

 

[[mybe_unused]]

[[maybe_unused]] void f([[maybe_unused]] bool thing1,

[[maybe_unused]] bool thing2) {

[[maybe_unused]] bool b = thing1 && thing2;

assert(b);

}

 

允许使用auto来声明无类型模板

示例:template<auto v>struct S;

保证的复制消除(guaranteed copy elision)
注释:prvalue和glvalue的含义已被修改,prvalue不再代表对象(object),而仅仅代表“初始化”(“initialization”)。返回prvalue的函数不再会复制对象(强制复制消除“mandatory copy elision”),并且有了新的从prvalue到glvalue的转换,叫做temporary materialization conversion。这个变化意味着复制消除是保证了的,而且甚至可以应用于不可移动或复制的类型。这允许你定义返回这种(不可移动或复制的)类型的函数。

更严格的表达式求值顺序
注释:对某些子表达式的求值顺序的规定更多了。这个变化的一个重要方面是函数的各个实参现在以不确定(indeterminate)的顺序的求值(也就是说没有交错 interleaving),而以前只是未指定的(unspecified)。注意重载运算符的求值顺序和调用方式有关:如果按照运算符的形式调用,就和相应的内置运算符的顺序相同;如果按照函数调用的形式调用,就和一般的函数调用相同(也就是不确定的 indeterminate)。

#include <map>

int main() {

 std::map<int, int> m;

 m[0] = m.size(); // #1

}

下面这些表达式的计算通用顺序

\1. a.b

\2. a->b

\3. a->*b

\4. a(b1, b2, b3)

\5. b @= a

\6. a[b]

\7. a << b

\8. a >> b

替换包含引用成员的类对象

注释:语言支持工具(“优化屏障optimisation barrier”),允许库重新使用存储(storage),并通过旧指针访问该存储(以前是不允许的)。(这是实现者的专家工具,不应在“正常”代码中出现)。

示例:

struct X { const int n; };

union U { X x; float f; };

void tong() {

 U u = {{ 1 }};

 u.f = 5.f;              // OK, creates new subobject of 'u' (9.5)

 X *p = new (&u.x) X {2};      // OK, creates new subobject of 'u'

 assert(p->n == 2);         // OK

 assert(*std::launder(&u.x.n) == 2); // OK

 assert(u.x.n == 2);       // undefined behavior, 'u.x' does not name new subobject

}

结构化绑定

示例:auto f()-> int(&)[2];

那么允许:auto [x,y]=f();或者auto& [x,y]=f();

另一个示例:struct S {int x1:2;volatile double y;};

那么S f();

存在const auto [x,y] = f();

其中x是const int而y是const volatile double

非类型模板参数可传入类的静态成员

在if和switch中可进行初始化

初始化对象时可以用花括号对其成员进行赋值

简化多层命名空间的写法

标准和非标准属性

在属性列表中,只有在属性的规范允许的情况下,才会出现省略号。后跟省略号的属性是包扩展。

不包含属性的属性说明符无效。属性标记在属性列表中的显示顺序并不重要。如果属性标记中包含满足标识符语法要求的关键字或替代标记,则将其视为标识符。

对属性标记中包含的任何标识符都不执行名称查找。属性标记确定属性参数子句(如果有)的附加要求

该特性过于抽象,无法举例

Constexpr if

注释:新的 if constexpr (condition) 语句根据常量表达式(constant expression)的值选择执行哪个分支。

在模板的实例中,只有在条件(condition)具有合适的值的时候,对应的分支才会实例化。

template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) {

   // ... handle p

   if constexpr (sizeof...(rs) > 0)

​    g(rs...); // never instantiated with an empty argument list.

  }

 

  extern int x;  // no definition of x required

  int f() {

   if constexpr (true)

​    return 0;

   else if (x)

​    return x;

   else

​    return -x;

  }

 

十六进制

double d = 0x1.4p3; // hex fraction 1.4 (decimal 1.25) scaled by 2^3, that is 10.0

 

#include <iostream>

int main()

{

 std::cout << 58.     << '\n'

​      << 4e2     << '\n'

​      << 123.456e-67 << '\n'

​      << .1E4f    << '\n'

​      << 0x10.1p0  << '\n'

​      << 0x1e5    << '\n'; // integer literal, not floating point literal

}

 

继承与改写构造函数 using B1::B1;//表示继承B1的构造函数


C++20新特性

允许捕获[=,this] 

struct S2 { void f(int i); };

void S2::f(int i) {

 [&, i]{ };     // OK

 [&, this, i]{ };  // OK, equivalent to [&, i]

 [&, &i]{ };    // error: i preceded by & when & is the default

 [=, *this]{ };   // OK

 [=, this]{ };   // error: this when = is the defaultOK, equivalent to [=]

 [i, i]{ };     // error: i repeated

 [this, *this]{ }; // error: this appears twice

}

 

仿函数宏

\#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__)

\#define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__)

\#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })

F(a, b, c) // replaced by f(0, a, b, c)

F()    // replaced by f(0)

G(a, b, c) // replaced by f(0, a, b, c)

G(a, )   // replaced by f(0, a)

G(a)    // replaced by f(0, a)

SDEF(foo);    // replaced by S foo;

SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 };

此外还有# 和## 宏

\#include <iostream>

 

// Make function factory and use it

\#define FUNCTION(name, a) int fun_##name() { return a;}

 

FUNCTION(abcd, 12)

FUNCTION(fff, 2)

FUNCTION(qqq, 23)

 

#undef FUNCTION

#define FUNCTION 34

#define OUTPUT(a) std::cout << "output: " #a << '\n'

 

// Using a macro in the definition of a later macro

#define WORD "Hello "

#define OUTER(...) WORD #__VA_ARGS__

 

int main()

{

  std::cout << "abcd: " << fun_abcd() << '\n';

  std::cout << "fff: " << fun_fff() << '\n';

  std::cout << "qqq: " << fun_qqq() << '\n';

 

  std::cout << FUNCTION << '\n';

  OUTPUT(million);        //note the lack of quotes

 

  std::cout << OUTER(World) << '\n';

  std::cout << OUTER(WORD World) << '\n';

}

 

委托初始化

struct A { int x; int y; int z; };

A a{.y = 2, .x = 1}; // error; designator order does not match declaration order

A b{.x = 1, .z = 2}; // ok, b.y initialized to 0

union u { int a; const char* b; };

u f = { .b = "asdf" };     // OK, active member of the union is b

u g = { .a = 1, .b = "asdf" }; // Error, only one initializer may be provided

//字符串的初始化

char a[] = "abc";

// equivalent to char a[4] = {'a', 'b', 'c', '\0'};

 

// unsigned char b[3] = "abc"; // Error: initializer string too long

unsigned char b[5]{"abc"};

// equivalent to unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};

 

wchar_t c[] = {L"кошка"}; // optional braces

// equivalent to wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};

 

通用的lambda表达式可用模板参数

auto f = []<typename T>(T const& x) {

T copy = x;

T::static_function();

using Iterator = typename T::iterator;

};

 

auto f = []<typename ...T>(T&& ...args) {

return foo(std::forward<T>(args)...);

};

 

默认成员初始化可以使用位域

int a;

const int b = 0;

struct S {

  // simple cases

  int x1 : 8 = 42;         // OK; "= 42" is brace-or-equal-initializer

  int x2 : 8 { 42 };        // OK; "{ 42 }" is brace-or-equal-initializer

  // ambiguities

  int y1 : true ? 8 : a = 42;   // OK; brace-or-equal-initializer is absent

  int y2 : true ? 8 : b = 42;   // error: cannot assign to const int

  int y3 : (true ? 8 : b) = 42;  // OK; "= 42" is brace-or-equal-initializer

  int z : 1 || new int { 0 };   // OK; brace-or-equal-initializer is absent

};

 

支持构造体模板参数推导

tuple t{tuple{1, 2}};  // Deduces tuple<int, int>

vector v{vector{1, 2}}; // Deduces vector<vector<int>>

 

修复成员的常量限定指针问题

struct X { void foo() const&; };

X{}.foo();    // this is okay

(X{}.*&X::foo)(); // this is ill-formed

这个bug可能导致限定丢失

C++概念的扩展

C++追加了一批新的词,用来描述模板编程

概念 概念是指一组指定的需求

通用形式如下:

template<模板参数列表>

concept 概念名称 = 约束表达式;

示例:

template<typename T>concept number = std::is_arithmetic<T>::value;

约束

一系列逻辑操作和操作数

约束一般有三种限制:连词、分离和原子约束(不可再分割的约束)

使用&& 做连词

使用 ||做分离

原子约束 参与约束定义的模板参数E不会因为逻辑或者逻辑表达式而被分离

而是作为整体处理的

要求条款

关键字requires

template<class T>

constexpr bool is_meowable = true;

 

template<class T>

constexpr bool is_purrable() { return true; }

 

template<class T>

void f(T) requires is_meowable<T>; // OK

 

template<class T>

void g(T) requires is_purrable<T>(); // error, is_purrable<T>() is not a primary expression

 

template<class T>

void h(T) requires (is_purrable<T>()); // OK

 

表达式lambda中的描述是未评估的

static decltype([] { }) f();

static decltype([] { }) f(); // invalid; return type mismatch

static decltype([] { }) g();

static decltype(g()) g(); // okay

static void h(decltype([] { }) *) { }

static void h(decltype([] { }) *) { }

h(nullptr); // ambiguous

using A = decltype([] { });

static void i(A *);

static void i(A *) { }

i(nullptr); // okay

三向比较运算符 <=>

对于 a <=> b

如果a<b 则 a <=> b < 0

如果a>b 则 a <=> b > 0

如果a=b 则 a <=> b == 0

简化隐式lambda捕获

 

for 可以使用范围描述来初始化

for (auto&& [first,second] : mymap) {

  // use first and second

}

 

for (T thing = foo(); auto& x : thing.items()) { /* ... */ } // OK

 

\#include <iostream>

\#include <vector>

 

int main() {

  std::vector<int> v = {0, 1, 2, 3, 4, 5};

 

  for (const int& i : v) // access by const reference

​    std::cout << i << ' ';

  std::cout << '\n';

 

  for (auto i : v) // access by value, the type of i is int

​    std::cout << i << ' ';

  std::cout << '\n';

 

  for (auto&& i : v) // access by forwarding reference, the type of i is int&

​    std::cout << i << ' ';

  std::cout << '\n';

 

  const auto& cv = v;

 

  for (auto&& i : cv) // access by f-d reference, the type of i is const int&

​    std::cout << i << ' ';

  std::cout << '\n';

 

  for (int n : {0, 1, 2, 3, 4, 5}) // the initializer may be a braced-init-list

​    std::cout << n << ' ';

  std::cout << '\n';

 

  int a[] = {0, 1, 2, 3, 4, 5};

  for (int n : a) // the initializer may be an array

​    std::cout << n << ' ';

  std::cout << '\n';

 

  for ([[maybe_unused]] int n : a) 

​    std::cout << 1 << ' '; // the loop variable need not be used

  std::cout << '\n';

 

  for (auto n = v.size(); auto i : v) // the init-statement (C++20)

​    std::cout << --n + i << ' ';

  std::cout << '\n';

 

}

 

默认可构造和可分配的无状态lambda

// in library.hpp

auto greater = [](auto x, auto y) { return x > y; };

// in user.cpp

// No need to care whether ‘greater‘ is a lambda or a function object

std::map<std::string, int, decltype(greater)> map;

 

常量与默认的复制构造函数不匹配

复制构造函数不是常量引用,则在带常量引用的模板类中会出现匹配失败

 struct MyType {

   MyType(MyType&); // no 'const'

 };

 

 

 

 template <typename T>

 struct Wrapper {

   Wrapper(const Wrapper&) = default;

   T t;

 };

 

 Wrapper<MyType> var; // fails to instantiate

 

专门化的访问检测

template<class T>

struct trait;

 

class class_ {

 template<class U>

 struct impl;

};

 

// Not allowed in standard C++ (impl is private)

// Not allowed in clang, icc

// Allowed in gcc, msvc

template<class U>

struct trait<class_::impl<U>>;

 

ADL(Argument Dependent Lookup)和模板函数不可见

ADL,即参数相关查找,是指在编译器对无限定域::的函数调用进行名字查找时,所应用的一种查找规则。

从类域,名称空间和全局域的顺序查找符号

int h;

void g();

namespace N {

​     struct A {};

​     template <class T> int f(T);

​     template <class T> int g(T);

​     template <class T> int h(T);

}

 

int x = f<N::A>(N::A()); // OK: lookup of f finds nothing,

​             // f treated as template name

int y = g<N::A>(N::A()); // OK: lookup of g finds a function,

​             // g treated as template name

int z = h<N::A>(N::A()); // error: "h<" does not begin a template-id

 

属性[[likely]] [[unlikely]] 

int f(int i)

{

  switch(i)

  {

​    case 1: [[fallthrough]];

​    [[likely]] case 2: return 1;

  }

  return 2;

}

属性likely表示语句会更可能的执行,让编译器优化路径

​     if (n > 0) [[likely]]

​    return x * pow(x, n - 1);

  else [[unlikely]]

​    return 1;

 

关键字typename的更多含义

template<class T> T::R f(); // OK, return type of a function declaration at global scope

template<class T> void f(T::R); // Ill-formed (no diagnostic required), attempt to declare a void variable template

template<class T> struct S {

using Ptr = PtrTraits<T>::Ptr; // OK, in a defining-type-id

T::R f(T::P p) { // OK, class scope

return static_cast<T::R>(p); // OK, type-id of a static_cast

}

auto g() -> S<T*>::Ptr;  // OK, trailing-return-type

};

template<typename T> void f() {

void (*pf)(T::X); // Variable pf of type void* initialized with T::X

void g(T::X); // Error: T::X at block scope does not denote a type

// (attempt to declare a void variable)

}

 

lambda初始化捕获中的包扩展

template<class... Args>

void f(Args... args) {

 auto lm = [&, args...] { return g(args...); };

 lm();

 

 auto lm2 = [...xs=std::move(args)] { return g(xs...); };

 lm2();

}

 

[[no_unique_address]]属性

可以不分配地址:

\#include <iostream>

 

struct Empty {}; // empty class

 

struct X {

  int i;

  Empty e;

};

 

struct Y {

  int i;

  [[no_unique_address]] Empty e;

};

 

struct Z {

  char c;

  [[no_unique_address]] Empty e1, e2;

};

 

struct W {

  char c[2];

  [[no_unique_address]] Empty e1, e2;

};

 

int main()

{

  // the size of any object of empty class type is at least 1

  static_assert(sizeof(Empty) >= 1);

 

  // at least one more byte is needed to give e a unique address

  static_assert(sizeof(X) >= sizeof(int) + 1);

 

  // empty member optimized out

  std::cout << "sizeof(Y) == sizeof(int) is " << std::boolalpha 

​       << (sizeof(Y) == sizeof(int)) << '\n';

 

  // e1 and e2 cannot share the same address because they have the

  // same type, even though they are marked with [[no_unique_address]]. 

  // However, either may share address with c.

  static_assert(sizeof(Z) >= 2);

 

  // e1 and e2 cannot have the same address, but one of them can share with

  // c[0] and the other with c[1]

  std::cout << "sizeof(W) == 2 is " << (sizeof(W) == 2) << '\n';

}

 

立即函数

consteval int sqr(int n) {

 return n*n;

}

constexpr int r = sqr(100); // OK

 

int x = 100;

int r2 = sqr(x);      // Error: Call does not produce a constant

 

consteval int sqrsqr(int n) {

 return sqr(sqr(n));    // Not a constant expression at this point, but OK

}

 

constexpr int dblsqr(int n) {

 return 2*sqr(n);     // Error: Enclosing function is not consteval

​              // and sqr(n) is not a constant

}

 

explicit(表达式)

struct A

{

  A(int) { }   // converting constructor

  A(int, int) { } // converting constructor (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: copy-initialization selects A::A(int)

  A a2(2);    // OK: direct-initialization selects A::A(int)

  A a3 {4, 5};  // OK: direct-list-initialization selects A::A(int, int)

  A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)

  A a5 = (A)1;  // OK: explicit cast performs static_cast

  if (a1) ;   // OK: A::operator bool()

  bool na1 = a1; // OK: copy-initialization selects A::operator bool()

  bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization

 

// B b1 = 1;   // error: copy-initialization does not consider B::B(int)

  B b2(2);    // OK: direct-initialization selects B::B(int)

  B b3 {4, 5};  // OK: direct-list-initialization selects B::B(int, int)

// B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)

  B b5 = (B)1;  // OK: explicit cast performs static_cast

  if (b2) ;   // OK: B::operator bool()

// bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()

  bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization

}

 

协程

\#include <coroutine>

\#include <iostream>

\#include <stdexcept>

\#include <thread>

 

auto switch_to_new_thread(std::jthread& out) {

 struct awaitable {

  std::jthread* p_out;

  bool await_ready() { return false; }

  void await_suspend(std::coroutine_handle<> h) {

   std::jthread& out = *p_out;

   if (out.joinable())

​    throw std::runtime_error("Output jthread parameter not empty");

   out = std::jthread([h] { h.resume(); });

   // Potential undefined behavior: accessing potentially destroyed *this

   // std::cout << "New thread ID: " << p_out->get_id() << '\n';

   std::cout << "New thread ID: " << out.get_id() << '\n'; // this is OK

  }

  void await_resume() {}

 };

 return awaitable{&out};

}

 

struct task{

 struct promise_type {

  task get_return_object() { return {}; }

  std::suspend_never initial_suspend() { return {}; }

  std::suspend_never final_suspend() noexcept { return {}; }

  void return_void() {}

  void unhandled_exception() {}

 };

};

 

task resuming_on_new_thread(std::jthread& out) {

 std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n';

 co_await switch_to_new_thread(out);

 // awaiter destroyed here

 std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n';

}

 

int main() {

 std::jthread out;

 resuming_on_new_thread(out);

}

模块

关键字module

关键字export

关键字import

声明模块 export module helloworld;

从模块中导出函数

export void hello() {   // export declaration

  std::cout << "Hello world!\n";

}

导入模块import helloworld;

导入后可以直接使用hello函数

export module A;  // declares the primary module interface unit for named module 'A'

 

// hello() will be visible by translations units importing 'A'

export char const* hello() { return "hello"; } 

 

// world() will NOT be visible.

char const* world() { return "world"; }

 

// Both one() and zero() will be visible.

export {

  int one() { return 1; }

  int zero() { return 0; }

}

 

// Exporting namespaces also works: hi::english() and hi::french() will be visible.

export namespace hi {

  char const* english() { return "Hi!"; }

  char const* french() { return "Salut!"; }

}

 

[[nodiscard]]

不丢弃属性,返回值不应该丢弃,否则告警

 

允许类模板参数通过别名模板进行推导

 

类型转换有时候会失败

int ii = {2.0}; // error: narrows

float f1 { x }; // error: might narrow

float f2 { 7 }; // OK: 7 can be exactly represented as a float

bool b = {"meow"}; // error: narrows

int f(int);

int a[] = { 2, f(2), f(2.0) }; // OK: the double-to-int conversion is not at the top level
posted @ 2023-03-25 15:53  Athenavi  阅读(20)  评论(0编辑  收藏  举报