revision #2: 修复从 gitbook 迁移源码后产生的代码格式问题

This commit is contained in:
Changkun Ou
2018-04-07 11:30:50 +02:00
parent 67d8f42967
commit 201c5603e3
11 changed files with 389 additions and 338 deletions

View File

@@ -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++ 的历史进程及其未来发展感兴趣的读者阅读。
## 二、主要入选特性 ## 二、主要入选特性

View File

@@ -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);
} }
``` ```

View File

@@ -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;
} }
``` ```

View File

@@ -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;
``` ```
## 总结 ## 总结

View File

@@ -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;
} }
``` ```
@@ -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;
} }
``` ```

View File

@@ -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: 将这部分内容补充为习题
## 进一步阅读的参考资料 ## 进一步阅读的参考资料

View File

@@ -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
View File

@@ -0,0 +1,3 @@
# 第八章 标准库: 文件系统
> TODO: 这部分内容为 C++17 新增

View File

@@ -1,4 +1,4 @@
# 第章 其他杂项 # 第章 其他杂项
> 内容修订中 > 内容修订中

52
book/appendix.md Normal file
View 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
...

View File

@@ -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,12 +95,7 @@
+ 原始字符串字面量 + 原始字符串字面量
+ 自定义字面量 + 自定义字面量
+ 数学库 + 数学库
- **第十章 展望: ~~C++17~~ C++20 简介** - [**第十章 展望: C++20 简介**](./10-cpp20.md)
+ ~~主要入选特性~~
+ ~~非类型模板参数的 `auto`~~
+ ~~`std::variant<>`~~
+ ~~变量声明的强化~~
+ 未入选特性
+ Concept + Concept
+ Range + Range
+ Module + Module