STL
atomic
#include
原子操作(atomic): 互斥量的加锁一般是针对一个代码段,而原子操作针对的一般都是一个变量。原子变量既不可复制亦不可移动。(1)它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。也就是他确保了在同一时刻只有唯一的线程对这个资源进行访问。这有点类似互斥对象对共享资源的访问的保护,但是原子操作更加接近底层,因而效率更高。是线程安全的。
(2)原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上,大家可以理解为这些原子类型内部自己加了锁。
(3)C++11中所有的原子类都是不允许拷贝、不允许Move的,atomic_flag也不例外。
(4)C++11 对常见的原子操作进行了抽象,定义出统一的接口,并根据编译选项/环境产生平台相关的实现。新标准将原子操作定义为atomic模板类的成员函数,囊括了绝大多数典型的操作——读、写、比较、交换等
contained type
atomic type
bool
atomic_bool
char
atomic_char
signed char
atomic_schar
unsigned char
atomic_uchar
short
atomic_short
unsigned short
atomic_ushort
int
atomic_int
unsigned int
atomic_uint
long
atomic_long
unsigned long
atomic_ulong
long long
atomic_llong
unsigned long long
atomic_ullong
wchar_t
atomic_wchar_t
char16_t
atomic_char16_t
char32_t
atomic_char32_t
intmax_t
atomic_intmax_t
uintmax_t
atomic_uintmax_t
int_leastN_t
atomic_int_leastN_t
uint_leastN_t
atomic_uint_leastN_t
int_fastN_t
atomic_int_fastN_t
uint_fastN_t
atomic_uint_fastN_t
intptr_t
atomic_intptr_t
uintptr_t
atomic_uintptr_t
size_t
atomic_size_t
ptrdiff_t
atomic_ptrdiff_t
(4)macro
macro
relative to types
ATOMIC_BOOL_LOCK_FREE
bool
ATOMIC_CHAR_LOCK_FREE
char
signed char
unsigned char
ATOMIC_SHORT_LOCK_FREE
short
unsigned short
ATOMIC_INT_LOCK_FREE
int
unsigned int
ATOMIC_LONG_LOCK_FREE
long
unsigned long
ATOMIC_LLONG_LOCK_FREE
long long
unsigned long long
ATOMIC_WCHAR_T_LOCK_FREE
wchar_t
ATOMIC_CHAR16_T_LOCK_FREE
char16_t
ATOMIC_CHAR32_T_LOCK_FREE
char32_t
ATOMIC_POINTER_LOCK_FREE
U*
(for any type U)
(5)memory_order:内存顺序
序号
值
意义
1
memory_order_relaxed
宽松模型,不对执行顺序做保证
2
memory_order_consume
当前线程中,满足happens-before原则。
当前线程中该原子的所有后续操作,必须在本条操作完成之后执行
3
memory_order_acquire
当前线程中,读操作满足happens-before原则。
所有后续的读操作必须在本操作完成后执行
4
memory_order_release
当前线程中,写操作满足happens-before原则。
所有后续的写操作必须在本操作完成后执行
5
memory_order_acq_rel
当前线程中,同时满足memory_order_acquire和memory_order_release
6
memory_order_seq_cst
最强约束。全部读写都按顺序执行
(6)Functions
(7)std::atomic的限制:trivially copyable(可平凡复制):一个类型如果是trivially copyable,则使用memcpy这种方式把它的数据从一个地方拷贝出来会得到相同的结果。
1.没有non-trivial 的拷贝构造函数
2.没有non-trivial的move构造函数
3.没有non-trivial的赋值操作符
4.没有non-trivial的move赋值操作符
5.有一个trivial的析构函数
std::atomic_flag:最简单的原子变量实例,是对于bool类型的变量进行原子操作,提供了标志的管理,标志有三种状态:clear、set和未初始化状态。
接口介绍:
(1)ATOMIC_FLAG_INIT:用于给atomic_flag变量赋初值,如果定义后为赋值,则状态是不确定的。被这个赋值后的状态为false。
(2)test_and_set() :接口函数,调用后状态变为true,并返回改变状态前的状态值。
(3)clear() : 接口函数,调用后状态变为false。
#include
#include
#include
#include
std::atomic_flag lock = ATOMIC_FLAG_INIT;
int gcnt = 0;
void f(int n)
{
for (int cnt = 0; cnt < 100; ++cnt) {
while (lock.test_and_set(std::memory_order_acquire)) // 获得锁
; // 自旋
std::cout << "Output from thread " << n << 'n';
gcnt++;
lock.clear(std::memory_order_release); // 释放锁
}
}
int main()
{
std::vector
for (int n = 0; n < 10; ++n) {
v.emplace_back(f, n);
}
for (auto& t : v) {
t.join();
}
}
自旋锁的解释:当某一个线程调用‘lock.test_and_set’时,即设置lock的状态为true,当另一个线程进入时,再次调用test_and_set时返回的状态为true,则一直在while循环中不断获取,即实现了等待,直到第一个线程调用clear将状态变为false。
std::atomic
(1)is_lock_free:通过这个接口判断是否需要加锁。如果是,则在多个线程访问该对象时不会导致线程阻塞(可能使用某种事务内存transactional memory方法实现lock-free的特性)。事实上该函数可以做为一个静态函数。所有指定相同类型T的atomic实例的is_lock_free函数都会返回相同值。
(2)store:赋值操作。operator=实际上内部调用了store,并返回d。
void store(T desr, memory_order m = memory_order_seq_cst) noexcept;
void store(T desr, memory_order m = memory_order_seq_cst) volatile noexcept;
T operator=(T d) noexcept;
T operator=(T d) volatile noexcept;
(3)load: 读取,加载并返回变量的值。operator T是load的简化版,内部调用的是load(memory_order_seq_cst)形式。
(4)exchange:交换,赋值后返回变量赋值前的值。exchange也称为read-modify-write操作。
T exchange(T desr, memory_order m = memory_order_seq_cst) volatile noexcept;
T exchange(T desr, memory_order m = memory_order_seq_cst) noexcept;
(5)compare_exchange_weak:这就是有名的CAS(Compare And Swap: 比较并交换)。操作是原子的,排它的。其它线程如果想要读取或修改该原子对象时,会等待先该操作完成。
(6)compare_exchange_strong:
进行compare时,与weak版一样,都是比较的物理内容。与weak版不同的是,strong版本不会返回伪false。即:原子对象所封装的值如果与expect在物理内容上相同,strong版本一定会返回true。其所付出的代价是:在某些需要循环检测的算法,或某些平台下,其性能较compare_exchange_weak要差。但对于某些不需要采用循环检测的算法而言, 通常采用compare_exchange_strong 更好。
std::atomic特化:
(1)fetch_add:该函数将原子对象封装的值加上v,同时返回原子对象的旧值。其功能用伪代码表示为:
// T is integral
T fetch_add(T v, memory_order m = memory_order_seq_cst) volatile noexcept;
T fetch_add(T v, memory_order m = memory_order_seq_cst) noexcept;
// T is pointer
T fetch_add(ptrdiff_t v, memory_order m = memory_order_seq_cst) volatile noexcept;
T fetch_add(ptrdiff_t v, memory_order m = memory_order_seq_cst) noexcept;
(2)fetch_sub:该函数将原子对象封装的值减去v,同时返回原子对象的旧值。其功能用伪代码表示为:
// T is integral
T fetch_sub(T v, memory_order m = memory_order_seq_cst) volatile noexcept;
T fetch_sub(T v, memory_order m = memory_order_seq_cst) noexcept;
// T is pointer
T fetch_sub(ptrdiff_t v, memory_order m = memory_order_seq_cst) volatile noexcept;
T fetch_sub(ptrdiff_t v, memory_order m = memory_order_seq_cst) noexcept;
(3)++, --, +=, -=:不管是基于整数的特化,还是指针特化,atomic均支持这四种操作。其用法与未封装时一样
独属于数值型特化的原子操作 - 位操作:
(1)fetch_and,fetch_or,fetch_xor:
位操作,将contained按指定方式进行位操作,并返回contained的旧值。
integral fetch_and(integral v, memory_order m = memory_order_seq_cst) volatile noexcept;
integral fetch_and(integral v, memory_order m = memory_order_seq_cst) noexcept;
integral fetch_or(integral v, memory_order m = memory_order_seq_cst) volatile noexcept;
integral fetch_or(integral v, memory_order m = memory_order_seq_cst) noexcept;
integral fetch_xor(integral v, memory_order m = memory_order_seq_cst) volatile noexcept;
integral fetch_xor(integral v, memory_order m = memory_order_seq_cst) noexcept;
(2)perator &=,operator |=,operator ^=:与相应的fetch_*操作不同的是,operator操作返回的是新值
T operator &=(T v) volatile noexcept {return fetch_and(v) & v;}
T operator &=(T v) noexcept {return fetch_and(v) & v;}
T operator |=(T v) volatile noexcept {return fetch_or(v) | v;}
T operator |=(T v) noexcept {return fetch_or(v) | v;}
T operator ^=(T v) volatile noexcept {return fetch_xor(v) ^ v;}
T operator ^=(T v) noexcept {return fetch_xor(v) ^ v;}
thread
#include
(1)
(2)tread函数:
get_id
获取线程 ID。
joinable
检查线程是否可被join。
join
Join 线程。
detach
Detach 线程
swap
native_handle
hardware_concurrency [static]
(1)接口函数:
函数
详解
示例
std::async(std::launch::async,provider);
(1)作用:在于将其获取到的函数立即在一个新的线程内进行异步启动。也就是一个线程启动函数。
(2)std::aysnc()返回:会返回一个std::future object类型的返回值,在std::future object中,我们可以取得线程返回值或异常信息。此外,std::future object类型的特化与线程函数的返回值一致。
(3)指定std::aysnc()的发射策略(launch strategy):
std::async的策略主要有两个:
1、std::launch::async : 立即尝试启动异步调用,如果在此处无法进行调用时,会返回一个std::system_error
2、std::launch::deferred : 延缓线程的启动,直到我们手动调用future::get()时,线程才会启动。
(1)示例1:std::aysnc(func1) //无参数形式
(2)示例2:
std::aysnc(func1,arg1,arg2) //向函数func1传递arg1,arg2;
(3)示例3:
void func1(int A = 0);
int func2();
int main()
{
std::future
//func1返回类型为void,future object的类型也为void
std::future
//func2返回类型为int,future object的类型也为int
}
(4)示例4:
int main()
{
//f1在这里就启动了,输出func1 start!
std::future
//f2在这里由于发射策略的原因,并没有启动
std::future
Sleep(3000);
std::cout << "3 seconds later!" << std::endl;
//三秒之后,由于调用future::get(),线程f2启动,输出func2 start!
f2.get();
return 0;
}
std::future
std::shared_future
类
std::thread
(1)作用:
(2)与async比较:
1、Class thread 没有发射策略,只要我们实例化Class thread的对象,系统就会尝试启动目标函数,如果无法启动目标函数,就会抛出std::system_error并携带差错resource_unavailable_try_again。
2、Class thread并不提供处理线程结果的接口
3、必须对线程的状态进行声明,等待其结束(join())或直接卸载(detach())
4、如果main()函数结束了,所有线程会被直接终止
示例1:
std::thread t1(Print, 1); //创建线程1
std::thread t2(Print, 2); //创建线程2
t1.join(); //等待线程1结束
t2.join(); //等待线程2结束
std::promise
packaged_task
(1)作用:实现了运行我们自由控制启动线程的启动时间,可以用于实现线程池
示例1:
std::packaged_task
Sleep(3000); //sleep3秒,当然,这里可以改成任何你需要的操作
task(); //3秒后启动线程
(2)互斥量与锁(Mutex & Lock):
std::mutex m_lock;
lock()
try_lock()
unlock()
try_lock_for()
try_lock_until()
lock_guard
(1)作用:
(2)使用:
std::lock_guard
std::lock_guard
(1)示例1:
std::mutex m_lock;
std::lock_guard
unique_lock
(1)作用:相对于Class lock_guard 来说,Class unique_lock 的特殊之处在于,可以让我们指定“何时”以及“如何”锁定和结果Mutex,此外,在Class unique_lock中,我们甚至可以用owns_lock()或bool()来查询目前Mutex是否会被锁住。
(3)条件变量(Condition Variable)
(4)
()
enable_if_t
std::enable_if_t, A>::value> * = nullptr>
enable_if
is_same
remove_cv_t
std::ref
C++ 关键词
class
用法
声明有作用域枚举类型
(C++11 起)
在模板声明中,class 可用于引入类型模板形参与模板模板形参若作用域中存在具有与某个类类型的名字相同的名字的函数或变量,则 class 可附于类名之前以消歧义,这产生一个详述类型说明符声明类
class Foo; // 类的前置声明
class Bar { // 类的定义
public:
Bar(int i) : m_i(i) {}
private:
int m_i;
};
template
void qux() {
T t;
}
int main()
{
Bar Bar(1);
class Bar Bar2(2); // 详述的类型
}
explicit
用法
explicit
(1)
explicit ( 表达式 )
(2)
(C++20 起)
表达式
-
经按语境转换为 bool 类型的常量表达式
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
// 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
}
friend
说明
友元声明出现于类体内,并向一个函数或另一个类授予对包含友元声明的类的私有及受保护成员的访问权。
语法
friend 函数声明
(1)
friend 函数定义
(2)
friend 详述类说明符 ;
(3)
friend 简单类型说明符 ;
friend typename-说明符 ;
(4)
(C++11 起)
inline
说明:
用法
inline namespace 定义 (C++11 起)函数与变量 (C++17 起)的 inline 说明符
export
用法
用于标记模板定义为被导出,这允许在其他翻译单元中声明但不定义同一模板。(1)
(C++11 前)
不使用并保留该关键词。
(C++11 起)
(C++20 前)
标记一个声明、一组声明或另一模块为当前模块所导出。
(C++20 起)
export 是可选的修饰符,模板被导出(用于声明类模板时,它也声明其所有成员被导出)。对被导出模板进行实例化的文件不需要包含其定义:声明即已充分。
export module name
导出模块export template < 形参列表 > 类声明
(1)
export template
class MyClass
{
public:
void memfun1(); // 被导出的函数
void memfun2(){ ..、} // 隐式内联不能被导出
...
void memfun3(); // 显式内联不能被导出
...
};
template
inline void MyClass
{
...
}
(2)
// helloworld.cpp
export module helloworld; // 模块声明
import
export void hello() { // export声明
std::cout << "Hello world!n";
}
// main.cpp
import helloworld; // import声明
int main() {
hello();
}
extern
用法
具有外部连接的静态存储期说明符语言连接说明显示模板实例化声明(或“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 invokedn"; // 它能使用 C++
}
concept(C++20 起)
约束与概念:
(1) 类模板,函数模板,以及非模板函数(常为类模板的成员),可以与约束(constraint)关联,它指定对模板实参的一些要求,这些要求可被用于选择最恰当的函数重载和模板特化。
(2) 这种要求的具名集合被称为概念(concept)。每个概念都是谓词,于编译时求值,并成为以之作为一项约束的模板接口的一部分:
const
说明:
用法
const 类型限定符const 限定的成员函数
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++20 前)
它必须不是协程
(C++20 起)
其函数体必须不是函数 try 块
(C++20 前)
函数体必须被弃置或预置,或只含有下列内容:
空语句(仅分号)static_assert 声明
不定义类或枚举的 typedef 声明及别名声明
using 声明using 指令
恰好一条 return 语句,若函数不是构造函数。
(C++14 前)
try 块asm 声明不进行初始化的变量定义。
(C++20 前)
(=default; 或 =delete; 的函数体不含任何上述内容。)
非字面类型的变量定义静态或线程存储期变量的定义函数体必须不含:
goto 语句
拥有除 case 和 default 之外的标号的语句
(C++14 起)
函数体非 =delete; 的 constexpr 构造函数必须满足下列额外要求:
对于 class 或 struct 的构造函数,每个子对象和每个非变体非 static 数据成员必须被初始化。若类是联合体式的类,对于其每个非空匿名联合体成员,必须恰好有一个变体成员被初始化对于非空 union 的构造函数,恰好有一个非静态数据成员被初始化
(C++20 前)
析构函数不能为 constexpr ,但能在常量表达式中调用平凡析构函数。
(C++20 前)
函数体非 =delete; 的 constexpr 析构函数必须满足下列额外要求:
每个用于销毁非静态数据成员与基类的析构函数必须为 constexpr 析构函数。
(C++20 起)
对于 constexpr 函数模板和类模板的 constexpr 函数成员,必须至少有一个特化满足上述要求。其他特化仍被认为是 constexpr,尽管常量表达式中不能出现这种函数的调用。
带初始化器的 if 语句
(2) constexpr if 语句 (C++17 起)每个被选用于初始化非静态成员和基类的构造函数必须是 constexpr 构造函数。其返回类型(若存在)必须是字面类型 (LiteralType)其每个参数都必须是字面类型 (LiteralType)对于构造函数与析构函数 (C++20 起),该类必须无虚基类至少存在一组实参值,使得函数的一个调用为核心常量表达式的被求值的子表达式(对于构造函数为足以用于常量初始化器) (C++14 起)。不要求对这点的诊断。其类型必须是字面类型 (LiteralType) 。它必须被立即初始化其初始化的全表达式,包括所有隐式转换、构造函数调用等,都必须是常量表达式(1)constexpr 声明说明符 (C++11 起) :指定变量或函数的值可在常量表达式中出现
(1)
constexpr int factorial(int n)
{
return n <= 1? 1 : (n * factorial(n - 1));
}
(2)
template
auto get_value(T t) {
if constexpr (std::is_pointer_v
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:见类型转换
dynamic_cast:见类型转换
const_cast < 新类型 > ( 表达式 )
dynamic_cast < 新类型 > ( 表达式 )
alignas(c++11)
头文件:
说明:指定类型或对象的对齐要求。说明符可应用于变量或非位域类数据成员的
声明,或可应用于 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
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)
头文件:
说明:std::max_align_t 通常是最大标量类型的同意词,在大多数平台上是 long
double ,而其对齐要求是 8 或 16 。
#include
#include
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
不允许原子块中的 复合语句 执行任何非 transaction_safe 的表达式或语句,或调用非 transaction_safe 的函数(这是编译时错误)。
(4)以除异常之外的任何方式(抵达结尾、goto、break、continue、return)离开原子块时,将提交事务。若用 std::longjmp 退出原子块则行为未定义。
事务安全的函数:
可在函数声明中用关键词 transaction_safe 将其显式声明为事务安全。
(1)
#include
#include
#include
int f()
{
static int i = 0;
synchronized { // 开始同步块
std::cout << i << " -> ";
++i; // 每次调用 f() 都获得 i 的唯一值
std::cout << i << 'n';
return i; // 结束同步块
}
}
int main()
{
std::vector
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 %dn", 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
若函数声明的 声明说明符序列 包含关键词 auto,则尾随返回类型可以省略,而编译器将从 return 语句中所用的表达式的类型推导出它。若返回类型使用的不是 decltype(auto),则推导遵循模板实参推导的规则进行。
(1)
template
auto add(T t, U u) { return t + u; } // 返回类型是 operator+(T, U) 的类型
(2)
// 在其所调用的函数返回引用的情况下
// 函数调用的完美转发必须用 decltype(auto)
template
decltype(auto) PerfectForward(F fun, Args&&..、args)
{
return fun(std::forward(args)...);
}
(3)
template // C++17 auto 形参声明
auto f() -> std::pair
{
return {n, n};
}
bool/break/switch/case/continue/switch/do/if/enum/false/for/goto
常规操作
char/char8_t(c++20)/char16_t(c++20)/char32_t(c++20)/double/float/int/long/short
default
用法
类名() = default ;
显式默认化的函数定义:令编译器为某个类生成特殊成员函数或比较运算符 (C++20 起)的显式指令switch 语句:用于声明默认情况标号
::(可选) delete 表达式
::(可选) delete [] 表达式
如果取代函数体而使用特殊语法 = delete ;,则该函数被定义为弃置的(deleted)。任何弃置函数的使用都是非良构的(程序无法编译)。
类名() = delete ;
弃置函数解分配函数:作为运算符式的函数名销毁先前由 new 表达式分配的对象,并释放获得的内存区域
try-catch:
说明:将一或多个异常处理块(catch 子句)与复合语句关联。
语法
try 复合语句 处理块序列 其中 处理块序列 是一或多个 处理块 的序列,它有下列语法:
catch ( attr(可选) 类型说明符序列 声明符 ) 复合语句
(1)
catch ( attr(可选) 类型说明符序列 抽象声明符(可选) ) 复合语句
(2)
catch ( ... ) 复合语句
(3)
协程:
协程是能暂停执行以在之后恢复的函数。协程是无栈的:它们通过返回到调用方暂停执行,并且从栈分离存储恢复所要求的数据。这允许编写异步执行的顺序代码(例如不使用显式的回调来处理非阻塞 I/O),还支持对惰性计算的无限序列上的算法及其他用途。
若函数的定义做下列任何内容之一,则它是协
co_wait(c++20)
用 co_await 运算符暂停执行,直至恢复
co_yield(c++20)
用关键词 co_yield 暂停执行并返回一个值
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
struct A { double x; };
const A* a;
decltype(a->x) y; // y 的类型是 double(其声明类型)
decltype((a->x)) z = y; // z 的类型是 const double&(左值表达式)
template
auto add(T t, U u) -> decltype(t + u) // 返回类型依赖于模板形参
{ // C++14 开始可以推导返回类型
return t+u;
}
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 声明
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(7);
assert((rwe & read) && (rwe & write) && (rwe & exec));
access_t x = static_cast(8.0); // C++17 起为未定义行为
access_t y = static_cast(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 << "redn"; break;
case Color::green: std::cout << "greenn"; break;
case Color::blue : std::cout << "bluen"; break;
}
// int n = r; // 错误:不存在从有作用域枚举到 int 的转换
int n = static_cast
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
}
">
template< class T >
struct is_trivially_copyable;
trivally copyable 需要满足3个条件:
连续的内存空间拷贝构造函数需要拷贝全部的bits (memcpy)没有虚函数和 noexcept constructorstd::alignment_of(c++11)
std::alignment_of_v (C++17 起)
提供等于 T 类型对齐要求的成员常量 value ,如同用 alignof 表达式获得。
#include
#include
class A {};
int main()
{
std::cout << std::alignment_of::value << 'n';
std::cout << std::alignment_of
std::cout << std::alignment_of_v
}
std::remove_all_extents_t
volatile
size_t
is_trivial
Explicit
override
snprintf
calloc
strdup
sqrt
Std::log
Math_errhandling
Volatile
wchar
Char32_t
Char16_t
.h/.hpp/.hxx
noexcept
noexcept(false)
Numeric_limit
epsilon
Initializer_list
stack
final
For_each
algorithm
throw
throw std::runtime_error( "File cannot be opened")
std::invalid_argument
logic_error
bad_typeid
bad_cast
bad_array_new_length
bad_alloc
std::system_error
bad_exception
std::out_of_range
std::exception
throw(std::runtime_error)
std::terminate()
std::is_nothrow_move_constructible
std::atexit
std::at_quick_exit
Logic_error
mutable
lambda
decltype
typeid
copy
sort
transform
Make_shared
move
Forward(cv)
Lock_guard
constexpr
Static_assert
defaule
register
Thread_local
inline
using
typename
template
asm
posix
is_arithmetic
[[noreturn]]
dlsym
拖尾返回类型->type
左右值引用
accumulate
Chrono::
Seconds
System_clock
Time_point
utility
Is_copy
智能指针
std::unique_ptr
类型的移动构造,移动赋值,"转换"移动构造和"转换"移动赋值
td::make_unique(10)
const_iterator
std::shared_ptr
std::weak_ptr
std::basic_ios类型的std::move()
std::basic_filebuf
多线程
std::thread
std::unique_lock
std::shared_lock
std::promise
std::future
std::shared_future
std::packaged_task
std::is_copy_constructible
std::is_trivially_move_constructible
std::is_trivially_copy_assignable
std::is_trivially_move_assignable
重载
operator delete
编译宏
#define subs(x) a ## x //合规
#define A(x) #x // 合规
defined, __LINE__, __FILE__, __DATE__, __TIME__, __STDC__, errno 和assert。
不能使用setjmp宏和longjmp函数。
std::hash
std::new_handler
offsetof
Placement new
std::bind
isdigit和isxdigit
const_iterator
equal_range
std::distance
std::srand(std::time(nullptr))
std::random_device rd;
std::default_random_engine eng{ rd() };
std::uniform_int_distribution
fgetpos, fopen, ftell, gets, perror, remove, rename
basic_filebuf
fstream
fseek、fsetpos或rewind
结构化绑定声明(c++17)
说明:
类似引用,结构化绑定是既存对象的别名。不同于引用的是,结构化绑定的类型不必为引用类型。
结构化绑定声明将 标识符列表 中的所有标识符,引入作为其外围作用域中的名字,并将它们绑定到
表达式 所指代的对象的各个子对象或元素。以此方式引入的绑定被称作结构化绑定。
attr(可选) cv-auto ref-运算符(可选) [ 标识符列表 ] = 表达式 ;(1)
attr(可选) cv-auto ref-运算符(可选) [ 标识符列表 ] { 表达式 } ;(2)
attr(可选) cv-auto ref-运算符(可选) [ 标识符列表 ] ( 表达式 ) ;(3)
说明:
attr
-
任意数量的属性的序列
cv-auto
-
可有 cv 限定的 auto 类型说明符,亦可包含存储类说明符 static 或 thread_local ;在 cv 限定符中包含 volatile 是被弃用的 (C++20 起)
ref-运算符
-
& 或 && 之一
标识符表
-
此声明所引入的各标识符的逗号分隔的列表
表达式
-
顶层没有逗号运算符的表达式(文法上为赋值表达式),且具有数组或非联合类之一的类型。
情况 1:绑定数组
标识符列表 中的每个标识符均成为指代数组的对应元素的左值。标识符的数量必须等于数组的元素数量。
int a[2] = {1,2};
auto [x,y] = a; // 创建 e[2],复制 a 到 e,然后 x 指代 e[0],y 指代 e[1]
auto& [xr, yr] = a; // xr 指代 a[0],yr 指代 a[1]
情况 2:绑定元组式类型
表达式 std::tuple_size
示例:
float x{};
char y{};
int z{};
std::tuple
const auto& [a,b,c] = tpl;
// a 指名指代 x 的结构化绑定;decltype(a) 为 float&
// b 指名指代 y 的结构化绑定;decltype(b) 为 char&&
// c 指名指代 tpl 的第 3 元素的结构化绑定;decltype(c) 为 const int
情况 3:绑定到数据成员
struct S {
int x1 : 2;
volatile double y1;
};
S f();
const auto [x, y] = f(); // x 是标识 2 位位域的 const int 左值
// y 是 const volatile double 左值