记录一下《现代C++教程》中的要点。
现代C++是指C++11之后的语法特性,如无特别说明,下面的语法特性均是C++11后才可使用。
作用:
NULL
赋空指针;使用:
char *a = nullptr;
NULL = (void*)0
或者NULL = 0
;void *
隐式转换到其他类型的指针,必须显式转换;NULL
就不知道是调用int
还是xxx *
类型的重载函数;nullptr
用于区分空指针和0,而弃用NULL
;作用:
使用:
constexpr
声明的对象: constexpr
组成的表达式)初始化;constexpr
声明的函数: using
指令、typedef
语句、static_assert
断言和return
语句外,不能出现其他语句;// 对象构造函数
constexpr int a = 1 + 2 + 3;// 函数
constexpr int fibonacci(const int n) {return n==1 || n==2 ? 1 : fibonacci(n-1) + fibonacci(n-2);
}int arr_1[a];
int arr_2[fibonacci(5)];
if( const std::vector::iterator itr = std::find(vec.begin(), vec.end(), 3);itr != vec.end()
)
{*itr = 4;
}
作用:
class object = {}
的形式进行初始化;使用:
std::initializer_list<参数类型> list
;class MagicFoo {public:std::vector vec;// 构造函数中使用MagicFoo(std::initializer_list list) {for (std::initializer_list::iterator it = list.begin();it != list.end(); ++it)vec.push_back(*it);}// 一般的成员函数中使用void foo(std::initializer_list list) {for (std::initializer_list::iterator it = list.begin();it != list.end(); ++it) vec.push_back(*it);}
};MagicFoo magicFoo = {1, 2, 3, 4, 5};
magicFoo.foo({6,7,8,9});
作用:
使用:
std::tuple f() {return std::make_tuple(1, 2.3, "456");
}// 自动将int、double和string的值绑定到x、y和z上
auto [x, y, z] = f();
作用:
使用:
decltype
搭配推导返回值类型,C++14后可以无需和decltype
搭配使用;auto i = 5; // i被推导为int
auto arr = new auto(10); // arr被推导为int *
auto it = vec.begin(); // it被推导为容器对应的迭代器类型template
auto add2(T x, U y) -> decltype(x+y){return x + y;
}// C++14之后
template
auto add2(T x, U y){return x + y;
}
作用:
使用:
decltype(表达式)
;decltype(auto)
作为函数的返回类型,它能自动推导转发函数或者封装函数的返回类型,也就是以调用其他函数作为返回类型的情况;auto x = 1;
auto y = 2;
// 用x+y表达式的类型定义z
decltype(x+y) z;// 比较x和int类型是否相同
if (std::is_same::value)std::cout << "type x == int" << std::endl;// 用decltype(auto)自动推导封装的返回类型
std::string look_up_a_string_1() {return lookup1();
}
decltype(auto) look_up_a_string_1() {return lookup1();
}
作用:
使用:
// 比较x和int类型是否相同
if (std::is_same::value)std::cout << "type x == int" << std::endl;
作用:
使用:
// 注意泛型的实例化在编译过程中就已经实现了
template
auto print_type_info(const T& t) {if constexpr (std::is_integral::value) {return t + 1;} else {return t + 0.001;}
}
int main() {std::cout << print_type_info(5) << std::endl;std::cout << print_type_info(3.14) << std::endl;
}/* 编译时的代码为:
int print_type_info(const int& t) {return t + 1;
}
double print_type_info(const double& t) {return t + 0.001;
}
int main() {std::cout << print_type_info(5) << std::endl;std::cout << print_type_info(3.14) << std::endl;
}
*/
作用:
使用:
for(auto element: 实现了迭代器的对象)
,然后在循环中可以用element
读取对象的每个元素;for(auto &element: 实现了迭代器的对象)
,然后在循环中可以用element
读写对象的每个元素;for (auto element : vec) {std::cout << element << std::endl; // read only
}
for (auto &element : vec) {element += 1; // read and write
}
作用:
extern
就代表在别处实例化,在本文件中使用但不重复实例化;使用:
template class 模板类名<实例化类型>
显式实例化;template 函数返回值类型 模板函数名<实例化类型>(参数类型)
显式实例化;extern template class 模板类名<实例化类型>
显式实例化;extern template 函数返回值类型 模板函数名<实例化类型>(参数类型)
显式实例化;// 在本编译文件中实例化模板
template class std::vector;
template int add(int t1, int t2);// 不在该当前编译文件中实例化模板
extern template class std::vector;
extern template int add(int t1, int t2);
作用:
使用:
std::vector> matrix;
>>
会被认为是右移运算符;作用:
typedef
;使用:
using namespace 命名空间名称
引入命名空间(传统C++);using 基类::基类成员
在子类中改变引用的基类成员的权限;using 别名 = 类型或者模板
指定别名;// 命名空间
using namespace std;
using namespace std::vector;// 在子类中改变基类成员的权限
class Base{
protected:int member;
};
class Derived: private Base { // 虽然是私有继承
public:using Base::member; // 但用using后member成为了子类的public成员
}// 指定普通类型别名
using ULL = unsigned long long; //typedef unsigned long long ULL;
// 指定函数类型别名
using func = void(*)(int, int); //typedef void(*func)(int, int);
// 指定模板别名
template
using mapInt = std::map;
mapInt bmap;
typedef
不能为模板定义别名,因为模板并不是类型,而是用来产生类型的;typedef
在定义函数指针别名时的写法很独特,在形式上并不规整;using
可以完全取代typedef
;作用:
使用:
sizeof()
函数搭配使用,实现模板参数的拆包;template
void magic(Ts... args) {// 输出参数的个数std::cout << sizeof...(args) << std::endl;
}// 用递归实现模板参数的拆包
template
void printf1(T0 value) {// 仅一个参数std::cout << value << std::endl;
}
template
void printf1(T value, Ts... args) {// 函数重载,多个参数std::cout << value << std::endl;printf1(args...);
}
int main() {printf1(1, 2, "123", 1.1);return 0;
}// C++17后可以这样实现拆包
template
void printf2(T0 t0, T... t) {// 一个或者多个参数std::cout << t0 << std::endl;if constexpr (sizeof...(t) > 0) printf2(t...);
}
作用:
使用:
template
class buffer_t {
public:T& alloc();void free(T& item);
private:T data[BufSize];
}
buffer_t buf; // 100 作为模板参数
作用:
使用:
class Base {
public:int value1;int value2;Base() {value1 = 1;}Base(int value) : Base() { // 委托Base() 构造函数value2 = value;}
};
作用:
使用:
using
关键字实现;class Base {
public:int value1;int value2;Base() {value1 = 1;}Base(int value) : Base() { // 委托Base() 构造函数value2 = value;}
};
class Subclass : public Base {
public:using Base::Base; // 继承构造
};
作用:
使用:
override
保证当前重载的是基类的虚函数;final
保证子类不会再覆盖当前类重载的虚函数,或者保证不会再派生子类;struct Base {virtual void foo(int);
};
struct SubClass: Base {virtual void foo(int) override; // 合法//virtual void foo(float) override; // 非法, 父类没有此虚函数
};struct Base {virtual void foo() final;
};
struct SubClass1 final: Base {
}; // 合法
struct SubClass2 : SubClass1 {
}; // 非法, SubClass1 已final
struct SubClass3: Base {void foo(); // 非法, foo 已final
};
作用:
使用:
函数定义 = default
则显式使用默认函数;函数定义 = delete
则显式禁用默认函数;class Magic {
public:Magic() = default; // 显式声明使用编译器生成的构造Magic& operator=(const Magic&) = delete; // 显式声明拒绝编译器生成构造Magic(int magic_number);
}
作用:
int
类型;使用:
enum class 枚举类名: 类型 {};
定义枚举类,就可以指定枚举类的类型;// 传统C++枚举类型
enum color_set {red, blue, green};color_set color1;
color1 = red;
color_set color2 = color1;
int i = color1; // 相当于int i = 0;
//color1 = 1; // 不允许将int赋值给enum
cout << color1; // 相当于cout << int(0);
//cin >> color1; // 不允许输入// 强类型枚举
enum class color_set1: unsigned int {red, blue, green};
enum class color_set2: int {red, blue, green};color_set1 color1 = red;
color_set2 color2 = red;
//color1 == color2 // 非法
//int i = color1; // 非法
//color1 == 0 // 非法
int
类型;int
不同,枚举变量的取值是有限制的,由它定义时的标识符数量决定;作用:
工作原理:
operator()
运算符;使用:
[捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 { // 函数体 }
,包括: [外部变量名]
,拷贝一个外部变量的副本传入函数;[&外部变量名]
,将外部变量的引用传入函数;[&]
或者[=]
,让编译器自动推导所需的引用捕获或者值捕获;// 和STL算法库中的函数搭配使用
sort(testdata.begin(), testdata.end(), [](int a, int b){ return a > b; });
for_each(a, a+4, [=](int x) { cout << x << " "; });
auto pos = find_if(coll.cbegin(), coll.cend(), [=](int i) { return i > x && i < y; });
vec_data.erase(std::remove_if(vec.date.begin(), vec_data.end(), [](int i) { return n < x;}), vec_data.end());// C++14后,可以在参数列表中使用auto
auto add = [](auto x, auto y) {return x + y;
};
cout << add(1, 4) << endl;
作用:
使用:
#include
;std::function<函数返回类型(函数参数类型)> 容器名 = 函数名或者lambda表达式
;#include int foo(int para) {return para;
}
// 封装函数foo
std::function func = foo;int important = 10;
// 封装lambda表达式
std::function func2 = [&](int value) -> int {return 1+value+important;
};std::cout << func(10) << std::endl;
std::cout << func2(10) << std::endl;
作用:
使用:
#include
;#include
void print(int a, int b, int c)
{std::cout << "a = " << a << ", b=" << b << ", c=" << c << "\n\n";
}// 将占位符2绑定到a,将2绑定到b,将占位符1绑定到c,生成新函数func(_1, _2)
auto func = std::bind(print, std::placeholders::_2, 2, std::placeholders::_1);
func(3, 4); // 相当于是print(4, 2, 3)
T &
;std::string & str
T &&
; std::move(左值变量)
将左值转换成右值,注意,这样会让原来的左值变量不可被访问;std::string && str
std::string lv1 = "string,"; // lv1 是一个左值
// std::string&& r1 = lv1; // 非法, 右值引用不能引用左值
std::string&& rv1 = std::move(lv1); // 合法, std::move 可以将左值转移为右值
std::cout << rv1 << std::endl; // string,const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的生命周期
// lv2 += "Test"; // 非法, 常量引用无法被修改
std::cout << lv2 << std::endl; // string,string,std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象生命周期
rv2 += "Test"; // 合法, 非常量引用能够修改临时变量
std::cout << rv2 << std::endl; // string,string,string,Test
std::move()
把它转为右值,但注意此时原来的左值会变空;作用:
使用:
std::forward<右值或左值引用类型>(右值或左值引用变量)
可以进行完美转发;void pass(T&& v) {// 总作为左值转发std::cout << " 普通传参: ";reference(v);// 强制转为右值转发std::cout << " std::move 传参: ";reference(std::move(v));// 使用std::forward转发std::cout << " std::forward 传参: ";reference(std::forward(v));// static_cast转换也符合引用坍缩规则std::cout << "static_cast 传参: ";reference(static_cast(v));
}
作用:
使用:
#include
;std::make_tuple(参数1,参数2...)
用于返回由各参数组成的std::tuple类型元组;std::get<元组下标>(元组变量)
用于获得元组变量对应下标的元素,可读写;std::tie(变量名1, 变量名2...) = 元组变量
用于将元组变量拆包,然后赋值给对应的变量名,可以用std::ignore
作变量名占位符;std::tuple_cat(元组变量1, 元组变量2)
用于合并两个元组;std::tuple_len(元组变量)
用于返回元组元素个数(元组长度);std::get<元素类型>(元组变量)
来获得元组中的该类型元素,但如果该类型的元素不唯一,则会有编译期错误;std::tuple_index(元组变量, 元组下标)
获得元组对应下标的元素;auto student = std::make_tuple(3.8, ’A’, " 张三");std::get<0>(student) = 3.6; // 修改元组的元素
cout << std::get<0>(student) << endl; // 读取元组的元素
std::get(student) = 3.6 // C++14后std::tie(gpa, std::ignore, name) = student; auto new_tuple = std::tuple_cat(get_student(1), std::move(t));for(int i = 0; i != tuple_len(new_tuple); ++i)// 运行期索引,C++17后std::cout << tuple_index(new_tuple, i) << std::endl;
作用:
使用:
#include
;std::make_shared<对象类型>(对象值)
可以生成一个对象,并返回它的shared_ptr指针,推荐这样使用;get()
可以获取原始指针而不增加引用计数;reset()
可以将当前shared_ptr的指向和引用计数清空,同时将其他指向同一个对象的shared_ptr的引用计数减一;std::make_shared pointer0(new int); // 不推荐这样使用
auto pointer = std::make_shared(10);
auto pointer2 = pointer; // 引用计数+1
auto pointer3 = pointer; // 引用计数+1int *p = pointer.get(); // 这样不会增加引用计数
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3pointer2.reset();
std::cout << "reset pointer2:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
std::cout << "pointer2.use_count() = "
<< pointer2.use_count() << std::endl; // pointer2 已reset; 0
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2pointer3.reset();
std::cout << "reset pointer3:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // pointer3 已reset; 0
作用:
使用:
#include
;std::make_unique<对象类型> 智能指针名(new 对象类型)
生成一个对象;std::make_unique<对象类型>(对象值)
生成一个对象,并返回它的unique_ptr指针,推荐这样使用;std::move()
把它指向的对象转移给别的std::unique_ptr;std::unique_ptr p1(std::make_unique());
// p1 不空, 输出
if (p1) p1->foo();
{std::unique_ptr p2(std::move(p1));// p2 不空, 输出f(*p2);// p2 不空, 输出if(p2) p2->foo();// p1 为空, 无输出if(p1) p1->foo();p1 = std::move(p2);// p2 为空, 无输出if(p2) p2->foo();std::cout << "p2 被销毁" << std::endl;
}
// p1 不空, 输出
if (p1) p1->foo();
// Foo 的实例会在离开作用域时被销毁
作用:
使用:
#include
;expired()
可以检查当前指向的对象是否还存在,返回布尔类型;use_count()
可以检查当前指向的对象的引用计数;lock()
可以返回当前指向对象的一个shared_ptr指针;shared_ptr sp(new int(10));
weak_ptr wp(sp);auto new_sp = wp.lock();
if(wp.expired()) {cout << "weak_ptr无效,资源已释放";
}
else {cout << "weak_ptr有效, *new_sp = " << *new_sp << endl;
}
作用:
使用:
#include
;std::thread 线程实例名(线程执行的函数名, 函数参数1, 函数参数2...)
创建一个执行函数的线程; std::bind()
一样,如果是值传递或者指针传递,则和普通函数一样,但如果是引用传递,则需要用std::ref(对象名)
代替直接使用对象名
作为实参;join()
用于阻塞创建线程实例的线程直至线程实例执行完毕;detach()
用于将线程实例从创建线程实例的线程中脱离开,成为守护线程,这样: std::future
在创建线程实例的线程中获得线程实例执行的结果;#include
#include
int main() {std::thread t([](){std::cout << "hello world." << std::endl;});// 阻塞main()直至t线程执行完毕t.join();return 0;
}
作用:
使用:
#include
;lock()
:当前线程尝试锁住该互斥量, unlock()
;unlock()
:当前线程释放该互斥量;try_lock()
:尝试锁住互斥量, unlock()
;std::lock_guard<互斥量类型> 名称(互斥量变量)
:mutex的RAII语法,用于: unlock()
释放;std::unique_lock<互斥量类型> 名称(互斥量变量)
:mutex的RAII语法,比lock_guard更加灵活,允许: #include // std::cout
#include // std::thread
#include // std::mutex// 调用mutex成员函数
volatile int counter(0); // non-atomic counter
std::mutex mtx; // locks access to counter
void attempt_10k_increases() {for (int i=0; i<10000; ++i) {if (mtx.try_lock()) { // only increase if currently not locked:++counter;mtx.unlock();}}
}// 使用lock_guard
void critical_section(int change_v) {static std::mutex mtx;std::lock_guard lock(mtx); // 相当于mtx.lock()// 执行竞争操作v = change_v;// 离开此作用域后mtx 会被释放
}// 使用unique_lock
void critical_section(int change_v) {static std::mutex mtx;std::unique_lock lock(mtx); // 相当于mtx.lock()// 执行竞争操作v = change_v;std::cout << v << std::endl;// 将锁进行释放lock.unlock(); // 相当于mtx.unlock()// 在此期间,任何人都可以抢夺v 的持有权// 开始另一组竞争操作,再次加锁lock.lock(); // 相当于mtx.lock()v += 1;std::cout << v << std::endl;
}
作用:
future.get()
或者thread.join()
;thread
执行函数功能;使用:
#include
;std::packaged_task<函数返回类型(函数参数类型)> 容器名(函数名或者lambda表达式)
创建一个std::packaged_task对象;std::futrue<函数返回类型> 容器名 = std::packaged_task对象.get_future()
创建一个获得std::packaged_task执行结果的future对象;get()
获得std::packaged_task执行结果;#include // std::cout
#include // std::packaged_task, std::future
#include // std::chrono::seconds
#include // std::thread, std::this_thread::sleep_for// count down taking a second for each value:
int countdown (int from, int to) {for (int i=from; i!=to; --i) {std::cout << i << '\n';std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout << "Finished!\n";return from - to;
}int main ()
{std::packaged_task task(countdown); // 设置 packaged_taskstd::future ret = task.get_future(); // 获得与 packaged_task 共享状态相关联的 future 对象.std::thread th(std::move(task), 10, 0); //创建一个新线程完成计数任务.int value = ret.get(); // 等待任务完成并获取结果.std::cout << "The countdown lasted for " << value << " seconds.\n";th.join();return 0;
}
作用:
使用:
#include
;std::condition_variable 条件变量名;
定义条件变量;条件变量对象.wait(互斥量对象)
,当前线程被阻塞,同时释放拥有的互斥量对象锁;条件变量对象.wait(互斥量对象, bool类型返回值函数)
,仅当函数返回值为true
,才将当前线程阻塞,同时释放拥有的互斥量对象锁;条件变量对象.notify_all()
,释放拥有的互斥量对象锁,唤醒所有wait()
的线程,并让它们竞争互斥信号量;条件变量对象.notify_one()
,释放拥有的互斥量对象锁,唤醒某一个wait()
的线程,并让它们竞争互斥信号量,但这样没有办法实现并发的竞争,效率较低,不太推荐在并发环境中使用;#include
#include
#include
#include
#include
#include
int main() {std::queue produced_nums;std::mutex mtx;std::condition_variable cv;bool notified = false; // 通知信号// 生产者auto producer = [&]() {for (int i = 0; ; i++) {std::this_thread::sleep_for(std::chrono::milliseconds(900));std::unique_lock lock(mtx);std::cout << "producing " << i << std::endl;produced_nums.push(i);notified = true;// 释放mtx,唤醒所有wait(mtx)的线程cv.notify_all();}};// 消费者auto consumer = [&]() {while (true) {std::unique_lock lock(mtx);while (!notified) { // 避免虚假唤醒// 释放mtx,等待别的线程唤醒自己cv.wait(lock);}// 消费者慢于生产者,则短暂取消锁,使得生产者有机会在消费者消费前继续生产lock.unlock();std::this_thread::sleep_for(std::chrono::milliseconds(1000));// 加锁消费lock.lock(); while (!produced_nums.empty()) {std::cout << "consuming " << produced_nums.front() << std::endl;produced_nums.pop();}notified = false;}};// 分别在不同的线程中运行std::thread p(producer);std::thread cs[2];for (int i = 0; i < 2; ++i) {cs[i] = std::thread(consumer);}p.join();for (int i = 0; i < 2; ++i) {cs[i].join();}return 0;
}
作用:
使用:
#include
;std::atomic::is_lock_free()
函数来检查T类型的对应原子类型是否支持真正的原子操作;fetch_add()
:加法操作,也有“+”运算符重载;fetch_sub()
:减法操作,也有“-”运算符重载;#include
#include
#include
std::atomic count = {0};
int main() {std::thread t1([](){count.fetch_add(1);});std::thread t2([](){count++; // 等价于fetch_addcount += 1; // 等价于fetch_add});t1.join();t2.join();std::cout << count << std::endl;return 0;
}
x = 1
,写x = 2
,读x = 2
;x = 3
,但不保证看到的x
写入1一定在写入2之前执行;c
在a
和b
的写入之后执行;x = 4
,但当前不一定读到;load()
、store()
和fetch_add()
增加参数std::memory_order_xxx
控制原子操作的一致性,进而降低同步的开销;std::memory_order_seq_cst
;counter.fetch_add(1, std::memory_order_seq_cst);
(2) 释放/获取模型:
std::memory_order_release
; store()
;std::memory_order_acquire
; load()
;std::memory_order_acq_rel
; 一个图例如下:
一个代码的例子如下:
a = 0;
b = 0;
c = 0;thread 1:
{a = 1;b.store(2, memory_order_relaxed);c.store(3, memory_order_release);
}thread 2:
{while (c.load(memory_order_acquire) != 3);// 以下 assert 永远不会失败assert(a == 1 && b == 2);assert(b.load(memory_order_relaxed) == 2);
}
std::memory_order_consume
; load()
;a = 0;
c = 0;thread 1:
{a = 1;c.store(3, memory_order_release);
}thread 2:
{while (c.load(memory_order_consume) != 3);assert(a == 1); // assert 可能失败也可能不失败
}
std::memory_order_relaxed
;作用:
使用:
函数返回值类型 函数名(函数参数类型) noexcept;
用于声明一个函数不会抛出异常;noexcept(表达式)
用于判断表达式是否有异常;// 可能抛出异常的函数
void may_throw() {throw true;
}
auto non_block_throw = []{may_throw();
};// 不抛出异常的函数
void no_throw() noexcept {return;
}
auto block_throw = []() noexcept {no_throw();
};
作用:
使用:
R"(字符串)"
将字符串定义为字符串字面量;""
后缀运算符可自定义整型字面量、浮点型字面量、字符串字面量和字符字面量为字符串字面量;// 字符串字面量自定义必须设置如下的参数列表
std::string operator"" _wow1(const char *wow1, size_t len) {return std::string(wow1)+"woooooooooow, amazing";
}
std::string operator"" _wow2 (unsigned long long i) {return std::to_string(i)+"woooooooooow, amazing";
}
int main() {auto str = "abc"_wow1;auto num = 1_wow2;std::cout << str << std::endl;std::cout << num << std::endl;return 0;
}
作用:
使用:
alignof(结构体)
:返回结构体的有效对齐值;struct alignas(有效对齐值) 结构体名{};
:修改结构体的有效对齐值,只能往大对齐;struct alignas(4) stTestAlign // 修改有效对齐值为4字节
{char a;char b;stTestAlign(){cout << "sizeof(stTestAlign) =" << sizeof(stTestAlign) << endl; //4cout << "alignof(stTestAlign) =" << alignof(stTestAlign) << endl; //4}
};struct stTestAlign
{char a;alignas(4) char b; // char原本是1字节,强制作为4字节对齐stTestAlign(){cout << "sizeof(stTestAlign) =" << sizeof(stTestAlign) << endl; //8cout << "alignof(stTestAlign) =" << alignof(stTestAlign) << endl; //4}
};
#pragma pack(n)
,由系统的位数和编译器决定;定义:
作用:
使用:
co_await Awaitable结构体
:调用一个Awaitable对象,由它内部定义决定它是挂起还是继续以及挂起和恢复时的行为: await_ready()
:询问Awaitable结构体是否已经准备好而不需要等待;await_suspend()
:传入一个coroutine_handle
类型的参数挂起Awaitable结构体;await_resume()
:协程重新运行时调用该函数,同时返回值,返回值的Promise;co_yield
:暂停执行并返回一个值;co_return
:完成执行并返回一个值;// 用回调函数实现init + 100
using call_back = std::function;
void Add100ByCallback(int init, call_back f) //init是传入的初始值,add之后的结果由回调函数f通知
{std::thread t([init, f]() {std::this_thread::sleep_for(std::chrono::seconds(5)); // sleep一下,假装很耗时f(init + 100); // 耗时的计算完成了,调用回调函数});t.detach();
}// 将回调函数封装成协程结构
struct Add100AWaitable
{Add100AWaitable(int init):init_(init) {}bool await_ready() const { return false; }int await_resume() { return result_; }void await_suspend(std::experimental::coroutine_handle<> handle){// 定义一个回调函数,在此函数中恢复协程auto f = [handle, this](int value) mutable {result_ = value;handle.resume(); // 这句是关键};Add100ByCallback(init_, f); }int init_; // 将参数存在这里int result_; // 将返回值存在这里
};// 调用协程计算init + 100,可以多次调用
Task Add100ByCoroutine(int init, call_back f)
{int ret = co_await Add100AWaitable(init);ret = co_await Add100AWaitable(ret);ret = co_await Add100AWaitable(ret);f(ret);
}
作用:
使用:
concepts
关键字;作用:
使用:
export
、module
和import
等关键字;