mirror of
https://github.com/changkun/modern-cpp-tutorial.git
synced 2025-12-17 12:44:40 +03:00
revision #2: 修复从 gitbook 迁移源码后产生的代码格式问题
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# 第九章 扩展主题:C++17 简介
|
# 第九章 展望: C++20 简介
|
||||||
|
|
||||||
> 内容修订中
|
> 内容修订中, 目前内容为第一版中对 C++17 的展望
|
||||||
|
|
||||||
## 一、本节内容
|
## 一、本节内容
|
||||||
|
|
||||||
@@ -8,14 +8,13 @@
|
|||||||
|
|
||||||
- 扩展主题: C++17 简介
|
- 扩展主题: C++17 简介
|
||||||
+ 主要入选特性
|
+ 主要入选特性
|
||||||
+ 非类型模板参数的 `auto`
|
+ 非类型模板参数的 `auto`
|
||||||
+ `std::variant<>`
|
+ `std::variant<>`
|
||||||
+ 结构化绑定(Structured bindings)
|
|
||||||
+ 变量声明的强化
|
+ 变量声明的强化
|
||||||
+ 未入选特性
|
+ 未入选特性
|
||||||
+ Concepts
|
+ Concepts
|
||||||
|
|
||||||
本节对即将到来的 C++17 进行介绍,几个月前(2016 年),目前为止,还没有一个正式发布的编译器来编译 C++17 特性的代码,本节作为扩展主题,供对 C++ 的历史进程及其未来发展感兴趣的读者阅读。
|
本章对即将到来的 C++17 进行介绍,几个月前(2016 年),目前为止,还没有一个正式发布的编译器来编译 C++17 特性的代码,本节作为扩展主题,供对 C++ 的历史进程及其未来发展感兴趣的读者阅读。
|
||||||
|
|
||||||
## 二、主要入选特性
|
## 二、主要入选特性
|
||||||
|
|
||||||
@@ -559,14 +559,14 @@ extern template class std::vector<MagicClass>; // 不在该编译文件中实例
|
|||||||
在传统 C++ 的编译器中,`>>`一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码:
|
在传统 C++ 的编译器中,`>>`一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::vector<std::vector<int>> mtx;
|
std::vector<std::vector<int>> mtx;
|
||||||
```
|
```
|
||||||
|
|
||||||
这在传统C++编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于下下面这种写法都能够通过编译:
|
这在传统C++编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于下下面这种写法都能够通过编译:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<bool T> SuckType;
|
template<bool T> SuckType;
|
||||||
std::vector<SuckType<(1>2)>> v; // 合法, 但不建议写出这样的代码
|
std::vector<SuckType<(1>2)>> v; // 合法, 但不建议写出这样的代码
|
||||||
```
|
```
|
||||||
|
|
||||||
### 类型别名模板
|
### 类型别名模板
|
||||||
@@ -599,7 +599,7 @@ using NewType = SuckType<std::vector, std::string>;
|
|||||||
```cpp
|
```cpp
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
auto add(T x, U y) -> decltype(x+y) {
|
auto add(T x, U y) -> decltype(x+y) {
|
||||||
return x+y
|
return x+y
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -610,7 +610,7 @@ return x+y
|
|||||||
```cpp
|
```cpp
|
||||||
template<typename T = int, typename U = int>
|
template<typename T = int, typename U = int>
|
||||||
auto add(T x, U y) -> decltype(x+y) {
|
auto add(T x, U y) -> decltype(x+y) {
|
||||||
return x+y
|
return x+y
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -626,9 +626,9 @@ template<typename... Ts> class Magic;
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
class Magic<int,
|
class Magic<int,
|
||||||
std::vector<int>,
|
std::vector<int>,
|
||||||
std::map<std::string,
|
std::map<std::string,
|
||||||
std::vector<int>>> darkMagic;
|
std::vector<int>>> darkMagic;
|
||||||
```
|
```
|
||||||
|
|
||||||
既然是任意形式,所以个数为0的模板参数也是可以的:`class Magic<> nothing;`。
|
既然是任意形式,所以个数为0的模板参数也是可以的:`class Magic<> nothing;`。
|
||||||
@@ -652,7 +652,7 @@ template<typename... Args> void printf(const std::string &str, Args... args);
|
|||||||
```cpp
|
```cpp
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void magic(Args... args) {
|
void magic(Args... args) {
|
||||||
std::cout << sizeof...(args) << std::endl;
|
std::cout << sizeof...(args) << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
我们可以传递任意个参数给 `magic` 函数:
|
我们可以传递任意个参数给 `magic` 函数:
|
||||||
@@ -710,14 +710,14 @@ void printf(T0 t0, T... t) {
|
|||||||
// 编译这个代码需要开启 -std=c++14
|
// 编译这个代码需要开启 -std=c++14
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
auto print(T value, Args... args) {
|
auto print(T value, Args... args) {
|
||||||
std::cout << value << std::endl;
|
std::cout << value << std::endl;
|
||||||
return std::initializer_list<T>{([&] {
|
return std::initializer_list<T>{([&] {
|
||||||
std::cout << args << std::endl;
|
std::cout << args << std::endl;
|
||||||
}(), value)...};
|
}(), value)...};
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
print(1, 2.1, "123");
|
print(1, 2.1, "123");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -753,20 +753,20 @@ C++11 引入了委托构造的概念,这使得构造函数可以在同一个
|
|||||||
```cpp
|
```cpp
|
||||||
class Base {
|
class Base {
|
||||||
public:
|
public:
|
||||||
int value1;
|
int value1;
|
||||||
int value2;
|
int value2;
|
||||||
Base() {
|
Base() {
|
||||||
value1 = 1;
|
value1 = 1;
|
||||||
}
|
}
|
||||||
Base(int value) : Base() { // 委托 Base() 构造函数
|
Base(int value) : Base() { // 委托 Base() 构造函数
|
||||||
value2 = 2;
|
value2 = 2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Base b(2);
|
Base b(2);
|
||||||
std::cout << b.value1 << std::endl;
|
std::cout << b.value1 << std::endl;
|
||||||
std::cout << b.value2 << std::endl;
|
std::cout << b.value2 << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -777,23 +777,23 @@ std::cout << b.value2 << std::endl;
|
|||||||
```cpp
|
```cpp
|
||||||
class Base {
|
class Base {
|
||||||
public:
|
public:
|
||||||
int value1;
|
int value1;
|
||||||
int value2;
|
int value2;
|
||||||
Base() {
|
Base() {
|
||||||
value1 = 1;
|
value1 = 1;
|
||||||
}
|
}
|
||||||
Base(int value) : Base() { // 委托 Base() 构造函数
|
Base(int value) : Base() { // 委托 Base() 构造函数
|
||||||
value2 = 2;
|
value2 = 2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class Subclass : public Base {
|
class Subclass : public Base {
|
||||||
public:
|
public:
|
||||||
using Base::Base; // 继承构造
|
using Base::Base; // 继承构造
|
||||||
};
|
};
|
||||||
int main() {
|
int main() {
|
||||||
Subclass s(3);
|
Subclass s(3);
|
||||||
std::cout << s.value1 << std::endl;
|
std::cout << s.value1 << std::endl;
|
||||||
std::cout << s.value2 << std::endl;
|
std::cout << s.value2 << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -803,10 +803,10 @@ std::cout << s.value2 << std::endl;
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct Base {
|
struct Base {
|
||||||
virtual void foo();
|
virtual void foo();
|
||||||
};
|
};
|
||||||
struct SubClass: Base {
|
struct SubClass: Base {
|
||||||
void foo();
|
void foo();
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -859,10 +859,10 @@ C++11 提供了上述需求的解决方案,允许显式的声明采用或拒
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
class Magic {
|
class Magic {
|
||||||
public:
|
public:
|
||||||
Magic() = default; // 显式声明使用编译器生成的构造
|
Magic() = default; // 显式声明使用编译器生成的构造
|
||||||
Magic& operator=(const Magic&) = delete; // 显式声明拒绝编译器生成构造
|
Magic& operator=(const Magic&) = delete; // 显式声明拒绝编译器生成构造
|
||||||
Magic(int magic_number);
|
Magic(int magic_number);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -874,10 +874,10 @@ C++11 引入了枚举类(enumaration class),并使用 `enum class` 的语
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
enum class new_enum : unsigned int {
|
enum class new_enum : unsigned int {
|
||||||
value1,
|
value1,
|
||||||
value2,
|
value2,
|
||||||
value3 = 100,
|
value3 = 100,
|
||||||
value4 = 100
|
value4 = 100
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -885,8 +885,8 @@ value4 = 100
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
if (new_enum::value3 == new_enum::value4) {
|
if (new_enum::value3 == new_enum::value4) {
|
||||||
// 会输出
|
// 会输出
|
||||||
std::cout << "new_enum::value3 == new_enum::value4" << std::endl;
|
std::cout << "new_enum::value3 == new_enum::value4" << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -899,7 +899,7 @@ std::cout << "new_enum::value3 == new_enum::value4" << std::endl;
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
|
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
|
||||||
{
|
{
|
||||||
return stream << static_cast<typename std::underlying_type<T>::type>(e);
|
return stream << static_cast<typename std::underlying_type<T>::type>(e);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ Lambda 表达式的基本语法如下:
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void learn_lambda_func_1() {
|
void learn_lambda_func_1() {
|
||||||
int value_1 = 1;
|
int value_1 = 1;
|
||||||
auto copy_value_1 = [value_1] {
|
auto copy_value_1 = [value_1] {
|
||||||
return value_1;
|
return value_1;
|
||||||
};
|
};
|
||||||
value_1 = 100;
|
value_1 = 100;
|
||||||
auto stored_value_1 = copy_value_1();
|
auto stored_value_1 = copy_value_1();
|
||||||
// 这时, stored_value_1 == 1, 而 value_1 == 100.
|
// 这时, stored_value_1 == 1, 而 value_1 == 100.
|
||||||
// 因为 copy_value_1 在创建时就保存了一份 value_1 的拷贝
|
// 因为 copy_value_1 在创建时就保存了一份 value_1 的拷贝
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -64,14 +64,14 @@ auto stored_value_1 = copy_value_1();
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void learn_lambda_func_2() {
|
void learn_lambda_func_2() {
|
||||||
int value_2 = 1;
|
int value_2 = 1;
|
||||||
auto copy_value_2 = [&value_2] {
|
auto copy_value_2 = [&value_2] {
|
||||||
return value_2;
|
return value_2;
|
||||||
};
|
};
|
||||||
value_2 = 100;
|
value_2 = 100;
|
||||||
auto stored_value_2 = copy_value_2();
|
auto stored_value_2 = copy_value_2();
|
||||||
// 这时, stored_value_2 == 100, value_1 == 100.
|
// 这时, stored_value_2 == 100, value_1 == 100.
|
||||||
// 因为 copy_value_2 保存的是引用
|
// 因为 copy_value_2 保存的是引用
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -99,12 +99,12 @@ C++14 给与了我们方便,允许捕获的成员用任意的表达式进行
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
auto important = std::make_unique<int>(1);
|
auto important = std::make_unique<int>(1);
|
||||||
auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {
|
auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {
|
||||||
return x+y+v1+(*v2);
|
return x+y+v1+(*v2);
|
||||||
};
|
};
|
||||||
std::cout << add(3,4) << std::endl;
|
std::cout << add(3,4) << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ return 0;
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
auto add = [](auto x, auto y) {
|
auto add = [](auto x, auto y) {
|
||||||
return x+y;
|
return x+y;
|
||||||
};
|
};
|
||||||
|
|
||||||
add(1, 2);
|
add(1, 2);
|
||||||
@@ -137,17 +137,17 @@ Lambda 表达式的本质是一个函数对象,当 Lambda 表达式的捕获
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using foo = void(int); // 定义函数指针, using 的使用见上一节中的别名语法
|
using foo = void(int); // 定义函数指针, using 的使用见上一节中的别名语法
|
||||||
void functional(foo f) {
|
void functional(foo f) {
|
||||||
f(1);
|
f(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
auto f = [](int value) {
|
auto f = [](int value) {
|
||||||
std::cout << value << std::endl;
|
std::cout << value << std::endl;
|
||||||
};
|
};
|
||||||
functional(f); // 函数指针调用
|
functional(f); // 函数指针调用
|
||||||
f(1); // lambda 表达式调用
|
f(1); // lambda 表达式调用
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -160,19 +160,19 @@ C++11 `std::function` 是一种通用、多态的函数封装,它的实例可
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int foo(int para) {
|
int foo(int para) {
|
||||||
return para;
|
return para;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// std::function 包装了一个返回值为 int, 参数为 int 的函数
|
// std::function 包装了一个返回值为 int, 参数为 int 的函数
|
||||||
std::function<int(int)> func = foo;
|
std::function<int(int)> func = foo;
|
||||||
|
|
||||||
int important = 10;
|
int important = 10;
|
||||||
std::function<int(int)> func2 = [&](int value) -> int {
|
std::function<int(int)> func2 = [&](int value) -> int {
|
||||||
return 1+value+important;
|
return 1+value+important;
|
||||||
};
|
};
|
||||||
std::cout << func(10) << std::endl;
|
std::cout << func(10) << std::endl;
|
||||||
std::cout << func2(10) << std::endl;
|
std::cout << func2(10) << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -182,13 +182,13 @@ std::cout << func2(10) << std::endl;
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
int foo(int a, int b, int c) {
|
int foo(int a, int b, int c) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
// 将参数1,2绑定到函数 foo 上,但是使用 std::placeholders::_1 来对第一个参数进行占位
|
// 将参数1,2绑定到函数 foo 上,但是使用 std::placeholders::_1 来对第一个参数进行占位
|
||||||
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
|
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
|
||||||
// 这时调用 bindFoo 时,只需要提供第一个参数即可
|
// 这时调用 bindFoo 时,只需要提供第一个参数即可
|
||||||
bindFoo(1);
|
bindFoo(1);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ bindFoo(1);
|
|||||||
```cpp
|
```cpp
|
||||||
std::vector<int> foo() {
|
std::vector<int> foo() {
|
||||||
std::vector<int> temp = {1, 2, 3, 4};
|
std::vector<int> temp = {1, 2, 3, 4};
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> v = foo();
|
std::vector<int> v = foo();
|
||||||
@@ -240,30 +240,30 @@ C++11 提供了 `std::move` 这个方法将左值参数无条件的转换为右
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
void reference(std::string& str) {
|
void reference(std::string& str) {
|
||||||
std::cout << "左值" << std::endl;
|
std::cout << "左值" << std::endl;
|
||||||
}
|
}
|
||||||
void reference(std::string&& str) {
|
void reference(std::string&& str) {
|
||||||
std::cout << "右值" << std::endl;
|
std::cout << "右值" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
std::string lv1 = "string,"; // lv1 是一个左值
|
std::string lv1 = "string,"; // lv1 是一个左值
|
||||||
// std::string&& r1 = s1; // 非法, 右值引用不能引用左值
|
// std::string&& r1 = s1; // 非法, 右值引用不能引用左值
|
||||||
std::string&& rv1 = std::move(lv1); // 合法, std::move可以将左值转移为右值
|
std::string&& rv1 = std::move(lv1); // 合法, std::move可以将左值转移为右值
|
||||||
std::cout << rv1 << std::endl; // string,
|
std::cout << rv1 << std::endl; // string,
|
||||||
|
|
||||||
const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的申明周期
|
const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的申明周期
|
||||||
// lv2 += "Test"; // 非法, 引用的右值无法被修改
|
// lv2 += "Test"; // 非法, 引用的右值无法被修改
|
||||||
std::cout << lv2 << std::endl; // string,string
|
std::cout << lv2 << std::endl; // string,string
|
||||||
|
|
||||||
std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象声明周期
|
std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象声明周期
|
||||||
rv2 += "Test"; // 合法, 非常量引用能够修改临时变量
|
rv2 += "Test"; // 合法, 非常量引用能够修改临时变量
|
||||||
std::cout << rv2 << std::endl; // string,string,string,
|
std::cout << rv2 << std::endl; // string,string,string,
|
||||||
|
|
||||||
reference(rv2); // 输出左值
|
reference(rv2); // 输出左值
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -279,25 +279,24 @@ return 0;
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
class A {
|
class A {
|
||||||
public:
|
public:
|
||||||
int *pointer;
|
int *pointer;
|
||||||
A():pointer(new int(1)) { std::cout << "构造" << pointer << std::endl; }
|
A():pointer(new int(1)) { std::cout << "构造" << pointer << std::endl; }
|
||||||
A(A& a):pointer(new int(*a.pointer)) { std::cout << "拷贝" << pointer << std::endl; } // 无意义的对象拷贝
|
A(A& a):pointer(new int(*a.pointer)) { std::cout << "拷贝" << pointer << std::endl; } // 无意义的对象拷贝
|
||||||
A(A&& a):pointer(a.pointer) { a.pointer = nullptr;std::cout << "移动" << pointer << std::endl; }
|
A(A&& a):pointer(a.pointer) { a.pointer = nullptr;std::cout << "移动" << pointer << std::endl; }
|
||||||
~A(){ std::cout << "析构" << pointer << std::endl; delete pointer; }
|
~A(){ std::cout << "析构" << pointer << std::endl; delete pointer; }
|
||||||
};
|
};
|
||||||
// 防止编译器优化
|
// 防止编译器优化
|
||||||
A return_rvalue(bool test) {
|
A return_rvalue(bool test) {
|
||||||
A a,b;
|
A a,b;
|
||||||
if(test) return a;
|
if(test) return a;
|
||||||
else return b;
|
else return b;
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
A obj = return_rvalue(false);
|
A obj = return_rvalue(false);
|
||||||
std::cout << "obj:" << std::endl;
|
std::cout << "obj:" << std::endl;
|
||||||
std::cout << obj.pointer << std::endl;
|
std::cout << obj.pointer << std::endl;
|
||||||
std::cout << *obj.pointer << std::endl;
|
std::cout << *obj.pointer << std::endl;
|
||||||
|
return 0;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -319,19 +318,19 @@ int main() {
|
|||||||
std::string str = "Hello world.";
|
std::string str = "Hello world.";
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
// 将使用 push_back(const T&), 即产生拷贝行为
|
// 将使用 push_back(const T&), 即产生拷贝行为
|
||||||
v.push_back(str);
|
v.push_back(str);
|
||||||
// 将输出 "str: Hello world."
|
// 将输出 "str: Hello world."
|
||||||
std::cout << "str: " << str << std::endl;
|
std::cout << "str: " << str << std::endl;
|
||||||
|
|
||||||
// 将使用 push_back(const T&&), 不会出现拷贝行为
|
// 将使用 push_back(const T&&), 不会出现拷贝行为
|
||||||
// 而整个字符串会被移动到 vector 中,所以有时候 std::move 会用来减少拷贝出现的开销
|
// 而整个字符串会被移动到 vector 中,所以有时候 std::move 会用来减少拷贝出现的开销
|
||||||
// 这步操作后, str 中的值会变为空
|
// 这步操作后, str 中的值会变为空
|
||||||
v.push_back(std::move(str));
|
v.push_back(std::move(str));
|
||||||
// 将输出 "str: "
|
// 将输出 "str: "
|
||||||
std::cout << "str: " << str << std::endl;
|
std::cout << "str: " << str << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -341,25 +340,25 @@ return 0;
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void reference(int& v) {
|
void reference(int& v) {
|
||||||
std::cout << "左值" << std::endl;
|
std::cout << "左值" << std::endl;
|
||||||
}
|
}
|
||||||
void reference(int&& v) {
|
void reference(int&& v) {
|
||||||
std::cout << "右值" << std::endl;
|
std::cout << "右值" << std::endl;
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void pass(T&& v) {
|
void pass(T&& v) {
|
||||||
std::cout << "普通传参:";
|
std::cout << "普通传参:";
|
||||||
reference(v); // 始终调用 reference(int& )
|
reference(v); // 始终调用 reference(int& )
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
std::cout << "传递右值:" << std::endl;
|
std::cout << "传递右值:" << std::endl;
|
||||||
pass(1); // 1是右值, 但输出左值
|
pass(1); // 1是右值, 但输出左值
|
||||||
|
|
||||||
std::cout << "传递左值:" << std::endl;
|
std::cout << "传递左值:" << std::endl;
|
||||||
int v = 1;
|
int v = 1;
|
||||||
pass(v); // r 是左引用, 输出左值
|
pass(v); // r 是左引用, 输出左值
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -382,30 +381,30 @@ return 0;
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
void reference(int& v) {
|
void reference(int& v) {
|
||||||
std::cout << "左值引用" << std::endl;
|
std::cout << "左值引用" << std::endl;
|
||||||
}
|
}
|
||||||
void reference(int&& v) {
|
void reference(int&& v) {
|
||||||
std::cout << "右值引用" << std::endl;
|
std::cout << "右值引用" << std::endl;
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void pass(T&& v) {
|
void pass(T&& v) {
|
||||||
std::cout << "普通传参:";
|
std::cout << "普通传参:";
|
||||||
reference(v);
|
reference(v);
|
||||||
std::cout << "std::move 传参:";
|
std::cout << "std::move 传参:";
|
||||||
reference(std::move(v));
|
reference(std::move(v));
|
||||||
std::cout << "std::forward 传参:";
|
std::cout << "std::forward 传参:";
|
||||||
reference(std::forward<T>(v));
|
reference(std::forward<T>(v));
|
||||||
|
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
std::cout << "传递右值:" << std::endl;
|
std::cout << "传递右值:" << std::endl;
|
||||||
pass(1);
|
pass(1);
|
||||||
|
|
||||||
std::cout << "传递左值:" << std::endl;
|
std::cout << "传递左值:" << std::endl;
|
||||||
int v = 1;
|
int v = 1;
|
||||||
pass(v);
|
pass(v);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
本节内容包括:
|
本节内容包括:
|
||||||
|
|
||||||
* 对标准库的扩充: 新增容器
|
* 对标准库的扩充: 新增容器
|
||||||
|
* `std::byte`
|
||||||
|
* `std::any` `std::optional` `std::variant`
|
||||||
|
* `std::string_view`
|
||||||
* `std::array`
|
* `std::array`
|
||||||
* `std::forward_list`
|
* `std::forward_list`
|
||||||
* `std::unordered_set`
|
* `std::unordered_set`
|
||||||
@@ -42,7 +45,7 @@ std::array<int, len> arr = {1,2,3,4}; // 非法, 数组大小参数必须是常
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void foo(int *p, int len) {
|
void foo(int *p, int len) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<int, 4> arr = {1,2,3,4};
|
std::array<int, 4> arr = {1,2,3,4};
|
||||||
@@ -79,27 +82,27 @@ C++11 引入了两组无序容器:`std::unordered_map`/`std::unordered_multima
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// 两组结构按同样的顺序初始化
|
// 两组结构按同样的顺序初始化
|
||||||
std::unordered_map<int, std::string> u = {
|
std::unordered_map<int, std::string> u = {
|
||||||
{1, "1"},
|
{1, "1"},
|
||||||
{3, "3"},
|
{3, "3"},
|
||||||
{2, "2"}
|
{2, "2"}
|
||||||
};
|
};
|
||||||
std::map<int, std::string> v = {
|
std::map<int, std::string> v = {
|
||||||
{1, "1"},
|
{1, "1"},
|
||||||
{3, "3"},
|
{3, "3"},
|
||||||
{2, "2"}
|
{2, "2"}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 分别对两组结构进行遍历
|
// 分别对两组结构进行遍历
|
||||||
std::cout << "std::unordered_map" << std::endl;
|
std::cout << "std::unordered_map" << std::endl;
|
||||||
for( const auto & n : u)
|
for( const auto & n : u)
|
||||||
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
|
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "std::map" << std::endl;
|
std::cout << "std::map" << std::endl;
|
||||||
for( const auto & n : v)
|
for( const auto & n : v)
|
||||||
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
|
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -138,33 +141,33 @@ auto get_student(int id)
|
|||||||
// 返回类型被推断为 std::tuple<double, char, std::string>
|
// 返回类型被推断为 std::tuple<double, char, std::string>
|
||||||
|
|
||||||
if (id == 0)
|
if (id == 0)
|
||||||
return std::make_tuple(3.8, 'A', "张三");
|
return std::make_tuple(3.8, 'A', "张三");
|
||||||
if (id == 1)
|
if (id == 1)
|
||||||
return std::make_tuple(2.9, 'C', "李四");
|
return std::make_tuple(2.9, 'C', "李四");
|
||||||
if (id == 2)
|
if (id == 2)
|
||||||
return std::make_tuple(1.7, 'D', "王五");
|
return std::make_tuple(1.7, 'D', "王五");
|
||||||
return std::make_tuple(0.0, 'D', "null");
|
return std::make_tuple(0.0, 'D', "null");
|
||||||
// 如果只写 0 会出现推断错误, 编译失败
|
// 如果只写 0 会出现推断错误, 编译失败
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
auto student = get_student(0);
|
auto student = get_student(0);
|
||||||
std::cout << "ID: 0, "
|
std::cout << "ID: 0, "
|
||||||
<< "GPA: " << std::get<0>(student) << ", "
|
<< "GPA: " << std::get<0>(student) << ", "
|
||||||
<< "成绩: " << std::get<1>(student) << ", "
|
<< "成绩: " << std::get<1>(student) << ", "
|
||||||
<< "姓名: " << std::get<2>(student) << '\n';
|
<< "姓名: " << std::get<2>(student) << '\n';
|
||||||
|
|
||||||
double gpa;
|
double gpa;
|
||||||
char grade;
|
char grade;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
// 元组进行拆包
|
// 元组进行拆包
|
||||||
std::tie(gpa, grade, name) = get_student(1);
|
std::tie(gpa, grade, name) = get_student(1);
|
||||||
std::cout << "ID: 1, "
|
std::cout << "ID: 1, "
|
||||||
<< "GPA: " << gpa << ", "
|
<< "GPA: " << gpa << ", "
|
||||||
<< "成绩: " << grade << ", "
|
<< "成绩: " << grade << ", "
|
||||||
<< "姓名: " << name << '\n';
|
<< "姓名: " << name << '\n';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -193,15 +196,15 @@ std::get<index>(t);
|
|||||||
template <size_t n, typename... T>
|
template <size_t n, typename... T>
|
||||||
boost::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
|
boost::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
|
||||||
if (i == n)
|
if (i == n)
|
||||||
return std::get<n>(tpl);
|
return std::get<n>(tpl);
|
||||||
else if (n == sizeof...(T) - 1)
|
else if (n == sizeof...(T) - 1)
|
||||||
throw std::out_of_range("越界.");
|
throw std::out_of_range("越界.");
|
||||||
else
|
else
|
||||||
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
|
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
|
||||||
}
|
}
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
boost::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
|
boost::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
|
||||||
return _tuple_index<0>(i, tpl);
|
return _tuple_index<0>(i, tpl);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -225,7 +228,7 @@ auto new_tuple = std::tuple_cat(get_student(1), std::move(t));
|
|||||||
```cpp
|
```cpp
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto tuple_len(T &tpl) {
|
auto tuple_len(T &tpl) {
|
||||||
return std::tuple_size<T>::value;
|
return std::tuple_size<T>::value;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -234,8 +237,8 @@ return std::tuple_size<T>::value;
|
|||||||
```cpp
|
```cpp
|
||||||
// 迭代
|
// 迭代
|
||||||
for(int i = 0; i != tuple_len(new_tuple); ++i)
|
for(int i = 0; i != tuple_len(new_tuple); ++i)
|
||||||
// 运行期索引
|
// 运行期索引
|
||||||
std::cout << tuple_index(i, new_tuple) << std::endl;
|
std::cout << tuple_index(i, new_tuple) << std::endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|||||||
@@ -35,17 +35,17 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
void foo(std::shared_ptr<int> i)
|
void foo(std::shared_ptr<int> i)
|
||||||
{
|
{
|
||||||
(*i)++;
|
(*i)++;
|
||||||
}
|
}
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// auto pointer = new int(10); // 非法, 不允许直接赋值
|
// auto pointer = new int(10); // 非法, 不允许直接赋值
|
||||||
// 构造了一个 std::shared_ptr
|
// 构造了一个 std::shared_ptr
|
||||||
auto pointer = std::make_shared<int>(10);
|
auto pointer = std::make_shared<int>(10);
|
||||||
foo(pointer);
|
foo(pointer);
|
||||||
std::cout << *pointer << std::endl; // 11
|
std::cout << *pointer << std::endl; // 11
|
||||||
// 离开作用域前,shared_ptr 会被析构,从而释放内存
|
// 离开作用域前,shared_ptr 会被析构,从而释放内存
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ std::unique_ptr<int> pointer2 = pointer; // 非法
|
|||||||
> ```cpp
|
> ```cpp
|
||||||
> template<typename T, typename ...Args>
|
> template<typename T, typename ...Args>
|
||||||
> std::unique_ptr<T> make_unique( Args&& ...args ) {
|
> std::unique_ptr<T> make_unique( Args&& ...args ) {
|
||||||
> return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
|
> return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
|
||||||
> }
|
> }
|
||||||
> ```
|
> ```
|
||||||
>
|
>
|
||||||
@@ -100,35 +100,35 @@ std::unique_ptr<int> pointer2 = pointer; // 非法
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
Foo() { std::cout << "Foo::Foo" << std::endl; }
|
Foo() { std::cout << "Foo::Foo" << std::endl; }
|
||||||
~Foo() { std::cout << "Foo::~Foo" << std::endl; }
|
~Foo() { std::cout << "Foo::~Foo" << std::endl; }
|
||||||
void foo() { std::cout << "Foo::foo" << std::endl; }
|
void foo() { std::cout << "Foo::foo" << std::endl; }
|
||||||
};
|
};
|
||||||
|
|
||||||
void f(const Foo &) {
|
void f(const Foo &) {
|
||||||
std::cout << "f(const Foo&)" << std::endl;
|
std::cout << "f(const Foo&)" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::unique_ptr<Foo> p1(std::make_unique<Foo>());
|
std::unique_ptr<Foo> p1(std::make_unique<Foo>());
|
||||||
// p1 不空, 输出
|
// p1 不空, 输出
|
||||||
if (p1) p1->foo();
|
if (p1) p1->foo();
|
||||||
{
|
{
|
||||||
std::unique_ptr<Foo> p2(std::move(p1));
|
std::unique_ptr<Foo> p2(std::move(p1));
|
||||||
// p2 不空, 输出
|
// p2 不空, 输出
|
||||||
f(*p2);
|
f(*p2);
|
||||||
// p2 不空, 输出
|
// p2 不空, 输出
|
||||||
if(p2) p2->foo();
|
if(p2) p2->foo();
|
||||||
// p1 为空, 无输出
|
// p1 为空, 无输出
|
||||||
if(p1) p1->foo();
|
if(p1) p1->foo();
|
||||||
p1 = std::move(p2);
|
p1 = std::move(p2);
|
||||||
// p2 为空, 无输出
|
// p2 为空, 无输出
|
||||||
if(p2) p2->foo();
|
if(p2) p2->foo();
|
||||||
std::cout << "p2 被销毁" << std::endl;
|
std::cout << "p2 被销毁" << std::endl;
|
||||||
}
|
}
|
||||||
// p1 不空, 输出
|
// p1 不空, 输出
|
||||||
if (p1) p1->foo();
|
if (p1) p1->foo();
|
||||||
// Foo 的实例会在离开作用域时被销毁
|
// Foo 的实例会在离开作用域时被销毁
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -141,22 +141,22 @@ struct A;
|
|||||||
struct B;
|
struct B;
|
||||||
|
|
||||||
struct A {
|
struct A {
|
||||||
std::shared_ptr<B> pointer;
|
std::shared_ptr<B> pointer;
|
||||||
~A() {
|
~A() {
|
||||||
std::cout << "A 被销毁" << std::end;
|
std::cout << "A 被销毁" << std::end;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
struct B {
|
struct B {
|
||||||
std::shared_ptr<A> pointer;
|
std::shared_ptr<A> pointer;
|
||||||
~B() {
|
~B() {
|
||||||
std::cout << "B 被销毁" << std::end;
|
std::cout << "B 被销毁" << std::end;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
int main() {
|
int main() {
|
||||||
auto a = std::make_shared<A>();
|
auto a = std::make_shared<A>();
|
||||||
auto b = std::make_shared<B>();
|
auto b = std::make_shared<B>();
|
||||||
a.pointer = b;
|
a.pointer = b;
|
||||||
b.pointer = a;
|
b.pointer = a;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -87,11 +87,11 @@ C++11 提供的正则表达式库操作 `std::string` 对象,模式 `std::rege
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
|
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
|
||||||
// 在 C++ 中 `\` 会被作为字符串内的转义符,为使 `\.` 作为正则表达式传递进去生效,需要对 `\` 进行二次转义,从而有 `\\.`
|
// 在 C++ 中 `\` 会被作为字符串内的转义符,为使 `\.` 作为正则表达式传递进去生效,需要对 `\` 进行二次转义,从而有 `\\.`
|
||||||
std::regex txt_regex("[a-z]+\\.txt");
|
std::regex txt_regex("[a-z]+\\.txt");
|
||||||
for (const auto &fname: fnames)
|
for (const auto &fname: fnames)
|
||||||
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
|
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -101,15 +101,15 @@ std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
|
|||||||
std::regex base_regex("([a-z]+)\\.txt");
|
std::regex base_regex("([a-z]+)\\.txt");
|
||||||
std::smatch base_match;
|
std::smatch base_match;
|
||||||
for(const auto &fname: fnames) {
|
for(const auto &fname: fnames) {
|
||||||
if (std::regex_match(fname, base_match, base_regex)) {
|
if (std::regex_match(fname, base_match, base_regex)) {
|
||||||
// sub_match 的第一个元素匹配整个字符串
|
// sub_match 的第一个元素匹配整个字符串
|
||||||
// sub_match 的第二个元素匹配了第一个括号表达式
|
// sub_match 的第二个元素匹配了第一个括号表达式
|
||||||
if (base_match.size() == 2) {
|
if (base_match.size() == 2) {
|
||||||
std::string base = base_match[1].str();
|
std::string base = base_match[1].str();
|
||||||
std::cout << "sub-match[0]: " << base_match[0].str() << std::endl;
|
std::cout << "sub-match[0]: " << base_match[0].str() << std::endl;
|
||||||
std::cout << fname << " sub-match[1]: " << base << std::endl;
|
std::cout << fname << " sub-match[1]: " << base << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ bar.txt sub-match[1]: bar
|
|||||||
|
|
||||||
> 本节提到的内容足以让我们开发编写一个简单的 Web 框架中关于URL匹配的功能。
|
> 本节提到的内容足以让我们开发编写一个简单的 Web 框架中关于URL匹配的功能。
|
||||||
|
|
||||||
> 关于这方面的开发和细节,可以通过项目课:[C++ 开发 Web 服务框架](https://www.shiyanlou.com/courses/568) 进行进一步学习。
|
> ~~关于这方面的开发和细节,可以通过项目课:[C++ 开发 Web 服务框架](https://www.shiyanlou.com/courses/568) 进行进一步学习。~~ TODO: 将这部分内容补充为习题
|
||||||
|
|
||||||
## 进一步阅读的参考资料
|
## 进一步阅读的参考资料
|
||||||
|
|
||||||
|
|||||||
116
book/7-thread.md
116
book/7-thread.md
@@ -20,12 +20,12 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
void foo() {
|
void foo() {
|
||||||
std::cout << "hello world" << std::endl;
|
std::cout << "hello world" << std::endl;
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
std::thread t(foo);
|
std::thread t(foo);
|
||||||
t.join();
|
t.join();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -39,13 +39,13 @@ return 0;
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void some_operation(const std::string &message) {
|
void some_operation(const std::string &message) {
|
||||||
static std::mutex mutex;
|
static std::mutex mutex;
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
|
||||||
// ...操作
|
// ...操作
|
||||||
|
|
||||||
// 当离开这个作用域的时候,互斥锁会被析构,同时unlock互斥锁
|
// 当离开这个作用域的时候,互斥锁会被析构,同时unlock互斥锁
|
||||||
// 因此这个函数内部的可以认为是临界区
|
// 因此这个函数内部的可以认为是临界区
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -62,14 +62,14 @@ std::mutex mtx;
|
|||||||
|
|
||||||
void block_area() {
|
void block_area() {
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
std::unique_lock<std::mutex> lock(mtx);
|
||||||
//...临界区
|
//...临界区
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
std::thread thd1(block_area);
|
std::thread thd1(block_area);
|
||||||
|
|
||||||
thd1.join();
|
thd1.join();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -92,15 +92,15 @@ return 0;
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// 将一个返回值为7的 lambda 表达式封装到 task 中
|
// 将一个返回值为7的 lambda 表达式封装到 task 中
|
||||||
// std::packaged_task 的模板参数为要封装函数的类型
|
// std::packaged_task 的模板参数为要封装函数的类型
|
||||||
std::packaged_task<int()> task([](){return 7;});
|
std::packaged_task<int()> task([](){return 7;});
|
||||||
// 获得 task 的 future
|
// 获得 task 的 future
|
||||||
std::future<int> result = task.get_future(); // 在一个线程中执行 task
|
std::future<int> result = task.get_future(); // 在一个线程中执行 task
|
||||||
std::thread(std::move(task)).detach(); std::cout << "Waiting...";
|
std::thread(std::move(task)).detach(); std::cout << "Waiting...";
|
||||||
result.wait();
|
result.wait();
|
||||||
// 输出执行结果
|
// 输出执行结果
|
||||||
std::cout << "Done!" << std:: endl << "Result is " << result.get() << '\n';
|
std::cout << "Done!" << std:: endl << "Result is " << result.get() << '\n';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -120,46 +120,46 @@ std::cout << "Done!" << std:: endl << "Result is " << result.get() << '\n';
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// 生产者数量
|
// 生产者数量
|
||||||
std::queue<int> produced_nums;
|
std::queue<int> produced_nums;
|
||||||
// 互斥锁
|
// 互斥锁
|
||||||
std::mutex m;
|
std::mutex m;
|
||||||
// 条件变量
|
// 条件变量
|
||||||
std::condition_variable cond_var;
|
std::condition_variable cond_var;
|
||||||
// 结束标志
|
// 结束标志
|
||||||
bool done = false;
|
bool done = false;
|
||||||
// 通知标志
|
// 通知标志
|
||||||
bool notified = false;
|
bool notified = false;
|
||||||
|
|
||||||
// 生产者线程
|
// 生产者线程
|
||||||
std::thread producer([&]() {
|
std::thread producer([&]() {
|
||||||
for (int i = 0; i < 5; ++i) {
|
for (int i = 0; i < 5; ++i) {
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
// 创建互斥锁
|
// 创建互斥锁
|
||||||
std::unique_lock<std::mutex> lock(m);
|
std::unique_lock<std::mutex> lock(m);
|
||||||
std::cout << "producing " << i << '\n';
|
std::cout << "producing " << i << '\n';
|
||||||
produced_nums.push(i);
|
produced_nums.push(i);
|
||||||
notified = true;
|
notified = true;
|
||||||
// 通知一个线程
|
// 通知一个线程
|
||||||
cond_var.notify_one();
|
cond_var.notify_one();
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
cond_var.notify_one();
|
cond_var.notify_one();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 消费者线程
|
// 消费者线程
|
||||||
std::thread consumer([&]() {
|
std::thread consumer([&]() {
|
||||||
std::unique_lock<std::mutex> lock(m);
|
std::unique_lock<std::mutex> lock(m);
|
||||||
while (!done) {
|
while (!done) {
|
||||||
while (!notified) { // 循环避免虚假唤醒
|
while (!notified) { // 循环避免虚假唤醒
|
||||||
cond_var.wait(lock);
|
cond_var.wait(lock);
|
||||||
}
|
}
|
||||||
while (!produced_nums.empty()) {
|
while (!produced_nums.empty()) {
|
||||||
std::cout << "consuming " << produced_nums.front() << '\n';
|
std::cout << "consuming " << produced_nums.front() << '\n';
|
||||||
produced_nums.pop();
|
produced_nums.pop();
|
||||||
}
|
}
|
||||||
notified = false;
|
notified = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
producer.join();
|
producer.join();
|
||||||
@@ -173,7 +173,7 @@ C++11 语言层提供了并发编程的相关支持,本节简单的介绍了 `
|
|||||||
|
|
||||||
> 本节提到的内容足以让我们使用不超过 100 行代码编写一个简单的线程池库
|
> 本节提到的内容足以让我们使用不超过 100 行代码编写一个简单的线程池库
|
||||||
>
|
>
|
||||||
> 关于这方面的使用技巧,可以通过项目课:[100 行 C++ 代码实现线程池](https://www.shiyanlou.com/teacher/courses/565) 进行进一步巩固学习。
|
> ~~关于这方面的使用技巧,可以通过项目课:[100 行 C++ 代码实现线程池](https://www.shiyanlou.com/teacher/courses/565) 进行进一步巩固学习。~~ TODO: 将这部分内容补充为习题
|
||||||
|
|
||||||
## 进一步阅读的参考资料
|
## 进一步阅读的参考资料
|
||||||
|
|
||||||
|
|||||||
3
book/8-filesystem.md
Normal file
3
book/8-filesystem.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# 第八章 标准库: 文件系统
|
||||||
|
|
||||||
|
> TODO: 这部分内容为 C++17 新增
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# 第八章 其他杂项
|
# 第九章 其他杂项
|
||||||
|
|
||||||
> 内容修订中
|
> 内容修订中
|
||||||
|
|
||||||
52
book/appendix.md
Normal file
52
book/appendix.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# 附录:进一步阅读的学习材料
|
||||||
|
|
||||||
|
首先,恭喜你阅读完本书 🎉 !笔者希望本书有提起你对现代 C++ 的兴趣。
|
||||||
|
|
||||||
|
正如本书引言部分提到的,本书只是一本带你快速领略现代 C++ 特性的读物,而非进阶学习实践『黑魔法』的内容。笔者当然也想到了这个需求,只是这样的内容非常艰深,鲜有受众。在此,笔者列出一些能够帮助你在此书基础之上进一步学习现代 C++ 的资料。
|
||||||
|
|
||||||
|
- [C++ 参考](http://en.cppreference.com/w)
|
||||||
|
- [CppCon YouTube 频道](https://www.youtube.com/user/CppCon/videos)
|
||||||
|
- [每位程序员都需要知道的内存知识(英文)](https://people.freebsd.org/~lstewart/articles/cpumemory.pdf)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
需要支持以下内容:
|
||||||
|
|
||||||
|
- 语言级 bug 与修订
|
||||||
|
|
||||||
|
+ 表达式评估顺序
|
||||||
|
+ 类型系统改进 Type Punning
|
||||||
|
+ 弃用和删除的功能
|
||||||
|
|
||||||
|
- 语言增强
|
||||||
|
|
||||||
|
+ [x] 结构化绑定 Structured bindings
|
||||||
|
+ [ ] inline 变量 `inline` variables
|
||||||
|
+ [x] 新的控制结构 `constexpr` if
|
||||||
|
+ [ ] 新的聚合规则
|
||||||
|
+ [ ] `constexpr` 改进
|
||||||
|
+ [ ] 强制性 RVO 和复制 elision, Guaranteed copy elision
|
||||||
|
- 模板增强
|
||||||
|
+ [x] 折叠表达式 Fold expressions
|
||||||
|
+ [ ] 类模板参数推导 Class template deduction
|
||||||
|
+ [ ] `auto` non-type template parameters
|
||||||
|
- 库增强
|
||||||
|
+ [ ] `std::string_view`, `std::byte`
|
||||||
|
+ [ ] `std::any`, `std::variant`, `std::optional`
|
||||||
|
+ [ ] 容器改进 container 改进
|
||||||
|
+ [ ] thread 并行 STL 算法
|
||||||
|
+ [ ] 文件系统库
|
||||||
|
- 其他特性
|
||||||
|
+ [ ] Selection statements with initializers
|
||||||
|
+ [ ] Compile-time conditional statements
|
||||||
|
+ [ ] Unary `statuc_assert`
|
||||||
|
+ [ ] Nested namespace deinitions
|
||||||
|
+ [ ] Preprocessor predicate for header testing
|
||||||
|
+ [ ] Polymorphic allocators and memory resources
|
||||||
|
+ [ ] Aligned `new`
|
||||||
|
+ [ ] Improved insertion and splicing for associative constrainers
|
||||||
|
+ [ ] Math special functions
|
||||||
|
+ [ ] Variable templates for metafunctions
|
||||||
|
+ [ ] Boolean logic metafunctions
|
||||||
|
...
|
||||||
|
|
||||||
19
book/toc.md
19
book/toc.md
@@ -86,8 +86,8 @@
|
|||||||
+ `std::future`
|
+ `std::future`
|
||||||
+ `std::packaged_task`
|
+ `std::packaged_task`
|
||||||
+ `std::condition_variable`
|
+ `std::condition_variable`
|
||||||
- [**第八章 标准库: 文件系统** TODO]
|
- [**第八章 标准库: 文件系统**](./8-filesystem.md)
|
||||||
- [**第九章 其他杂项**](./8-others.md)
|
- [**第九章 其他杂项**](./9-others.md)
|
||||||
+ 新类型
|
+ 新类型
|
||||||
+ `long long int`
|
+ `long long int`
|
||||||
+ `noexcept` 的修饰和操作
|
+ `noexcept` 的修饰和操作
|
||||||
@@ -95,16 +95,11 @@
|
|||||||
+ 原始字符串字面量
|
+ 原始字符串字面量
|
||||||
+ 自定义字面量
|
+ 自定义字面量
|
||||||
+ 数学库
|
+ 数学库
|
||||||
- **第十章 展望: ~~C++17~~ C++20 简介**
|
- [**第十章 展望: C++20 简介**](./10-cpp20.md)
|
||||||
+ ~~主要入选特性~~
|
+ Concept
|
||||||
+ ~~非类型模板参数的 `auto`~~
|
+ Range
|
||||||
+ ~~`std::variant<>`~~
|
+ Module
|
||||||
+ ~~变量声明的强化~~
|
+ Coroutine
|
||||||
+ 未入选特性
|
|
||||||
+ Concept
|
|
||||||
+ Range
|
|
||||||
+ Module
|
|
||||||
+ Coroutine
|
|
||||||
|
|
||||||
|
|
||||||
返回目录 | 上一章 | [下一章:序言](./0-preface.md)
|
返回目录 | 上一章 | [下一章:序言](./0-preface.md)
|
||||||
|
|||||||
Reference in New Issue
Block a user