mirror of
https://github.com/changkun/modern-cpp-tutorial.git
synced 2025-12-18 21:24:39 +03:00
Compare commits
126 Commits
v2.0.0-alp
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01b64d415e | ||
|
|
7b57e5e5a1 | ||
|
|
2659a5f207 | ||
|
|
af45678ad4 | ||
|
|
78b1cdf6bb | ||
|
|
5c50dbc591 | ||
|
|
54e99e1461 | ||
|
|
bdf0e899a7 | ||
|
|
ff6ee89e2a | ||
|
|
e86e4a4b94 | ||
|
|
eca0b92307 | ||
|
|
725df6b753 | ||
|
|
4228ff2afb | ||
|
|
a1c4184b8d | ||
|
|
de86a78627 | ||
|
|
f6538da3d6 | ||
|
|
8182184f2a | ||
|
|
237c990510 | ||
|
|
2e9eed9ecc | ||
|
|
e50e3c64f2 | ||
|
|
41b88c861e | ||
|
|
25a8fd2bc4 | ||
|
|
b22404503e | ||
|
|
d740ee8b7c | ||
|
|
0616cb816d | ||
|
|
dd7b707acf | ||
|
|
97b6cc75f7 | ||
|
|
66d33aa2f4 | ||
|
|
4dfd34e8a0 | ||
|
|
cfda52200a | ||
|
|
b5f5940239 | ||
|
|
80db4e3d4a | ||
|
|
e276dac7d1 | ||
|
|
aca2d372d7 | ||
|
|
afdc39431f | ||
|
|
1db84fe462 | ||
|
|
847b53632d | ||
|
|
7e6ada9b7c | ||
|
|
b386fff302 | ||
|
|
e5d90bd610 | ||
|
|
761a408d82 | ||
|
|
417d45806b | ||
|
|
1ce9b16867 | ||
|
|
37401ada8b | ||
|
|
c172682723 | ||
|
|
19ceeb7b4f | ||
|
|
7f46de1a6c | ||
|
|
9ff1deb882 | ||
|
|
55da3fd452 | ||
|
|
93ce6490a6 | ||
|
|
b0f6f30988 | ||
|
|
309f1f3731 | ||
|
|
6f5615b971 | ||
|
|
e202554f16 | ||
|
|
529399ffcd | ||
|
|
ed88f4efbb | ||
|
|
d472ff1421 | ||
|
|
7eeaa7ae4e | ||
|
|
068840bb72 | ||
|
|
7f7d7086a9 | ||
|
|
69aa4f0bdf | ||
|
|
711694e0a2 | ||
|
|
645fa62677 | ||
|
|
731a219d89 | ||
|
|
45bef893a7 | ||
|
|
ad4b9580a5 | ||
|
|
5e2531058c | ||
|
|
4cb055f629 | ||
|
|
a386449dec | ||
|
|
aa4d1eead5 | ||
|
|
25aa347428 | ||
|
|
9ded3a2967 | ||
|
|
6b73213e78 | ||
|
|
a5445b7c55 | ||
|
|
7eceaf1bee | ||
|
|
054119f69c | ||
|
|
91739f178c | ||
|
|
45618ad676 | ||
|
|
938dcd9e95 | ||
|
|
7ab24fe51c | ||
|
|
66297323ad | ||
|
|
6b911261fa | ||
|
|
6dc07f6611 | ||
|
|
1e78a01ac0 | ||
|
|
56de7fa327 | ||
|
|
6a3d8d7103 | ||
|
|
c239ef1b2d | ||
|
|
0f8819f4f9 | ||
|
|
030b43e22b | ||
|
|
659a3801a5 | ||
|
|
c9f8eb70fd | ||
|
|
212f4f3503 | ||
|
|
ff05988742 | ||
|
|
518c6e97d7 | ||
|
|
a3f9aaa394 | ||
|
|
32bdf85ebd | ||
|
|
4de9eb49f7 | ||
|
|
9dbdd53f2e | ||
|
|
93df232f1b | ||
|
|
c4c7ad14ee | ||
|
|
c015324c5c | ||
|
|
1421f372e0 | ||
|
|
bfd2f7caaf | ||
|
|
5beea9797a | ||
|
|
1dddfce585 | ||
|
|
79b6d5b567 | ||
|
|
3abc907bb4 | ||
|
|
f1c1e06178 | ||
|
|
0f39561c5a | ||
|
|
b5e90c4fdf | ||
|
|
1fcfd36964 | ||
|
|
b7debcc558 | ||
|
|
d31da4caf9 | ||
|
|
a5d1563609 | ||
|
|
89060d4b96 | ||
|
|
a98686bd82 | ||
|
|
e5450c767c | ||
|
|
bc4ff8f614 | ||
|
|
04420e835e | ||
|
|
8767bb89c4 | ||
|
|
0aa031f0f6 | ||
|
|
934eb895c5 | ||
|
|
6914acc324 | ||
|
|
4b1181f233 | ||
|
|
e784ed003e | ||
|
|
d428b44c56 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [changkun] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
otechie: # Replace with a single Otechie username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
29
.github/workflows/website.yml
vendored
Normal file
29
.github/workflows/website.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: Website
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Website
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: build
|
||||||
|
env:
|
||||||
|
USER: ${{ secrets.SERVER_USER }}
|
||||||
|
TARGET: ${{ secrets.SERVER_PATH }}
|
||||||
|
KEY: ${{ secrets.SERVER_KEY }}
|
||||||
|
DOMAIN: ${{ secrets.SERVER_DOMAIN }}
|
||||||
|
run: |
|
||||||
|
make build
|
||||||
|
mkdir ~/.ssh
|
||||||
|
echo "$KEY" | tr -d '\r' > ~/.ssh/id_ed25519
|
||||||
|
chmod 400 ~/.ssh/id_ed25519
|
||||||
|
eval "$(ssh-agent -s)"
|
||||||
|
ssh-add ~/.ssh/id_ed25519
|
||||||
|
ssh-keyscan -H $DOMAIN >> ~/.ssh/known_hosts
|
||||||
|
scp -r website/public/modern-cpp/* $USER@$DOMAIN:$TARGET
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -40,7 +40,11 @@ website/src/modern-cpp/zh-cn/*
|
|||||||
website/src/modern-cpp/en-us/*
|
website/src/modern-cpp/en-us/*
|
||||||
website/src/modern-cpp/exercises
|
website/src/modern-cpp/exercises
|
||||||
website/src/modern-cpp/code
|
website/src/modern-cpp/code
|
||||||
|
website/src/modern-cpp/about/donate.md
|
||||||
|
website/src/modern-cpp/assets/alipay.jpg
|
||||||
website/src/modern-cpp/assets/cover-2nd-en.png
|
website/src/modern-cpp/assets/cover-2nd-en.png
|
||||||
website/src/modern-cpp/assets/cover-2nd.png
|
website/src/modern-cpp/assets/cover-2nd.png
|
||||||
website/src/modern-cpp/assets/qq-group.png
|
website/src/modern-cpp/assets/cover-2nd-en-logo.png
|
||||||
website/src/modern-cpp/assets/figures/*
|
website/src/modern-cpp/assets/cover-2nd-logo.png
|
||||||
|
website/src/modern-cpp/assets/figures/*
|
||||||
|
website/src/modern-cpp/assets/wechat.jpg
|
||||||
|
|||||||
18
.travis.yml
18
.travis.yml
@@ -1,18 +0,0 @@
|
|||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- openssl aes-256-cbc -K $encrypted_9157553ce13c_key -iv $encrypted_9157553ce13c_iv -in .travis/travis.enc -out ~/.ssh/id_rsa -d
|
|
||||||
- chmod 600 ~/.ssh/id_rsa
|
|
||||||
- git config --global user.name "Changkun Ou"
|
|
||||||
- git config --global user.email "hi@changkun.us"
|
|
||||||
|
|
||||||
addons:
|
|
||||||
ssh_known_hosts:
|
|
||||||
- changkun.de
|
|
||||||
|
|
||||||
script:
|
|
||||||
- make build
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
scp -r website/public/modern-cpp/* $encrypted_server_user@changkun.de:$encrypted_server_path
|
|
||||||
Binary file not shown.
@@ -4,32 +4,32 @@
|
|||||||
|
|
||||||
C++ 11/14/17 issue is used to track the principle description error, `typo` error, and the questions to the author of the book.
|
C++ 11/14/17 issue is used to track the principle description error, `typo` error, and the questions to the author of the book.
|
||||||
|
|
||||||
- Usually, you may encounter typos, semantic errors, grammatical errors, and etc. These are all `typo` errors. If an error has caused some obstacles to your reading and you strongly believe that the `typo` will also affect others reading. Then you are very welcome to [submit issue](https://github.com/changkun/modern-cpp-tutorial/issues) to report the `typo` error.
|
- Usually, you may encounter typos, semantic errors, grammatical errors, and etc. These are all `typo` errors. If an error has caused some obstacles to your reading and you strongly believe that the `typo` will also affect others reading, then you are very welcome to [submit issue](https://github.com/changkun/modern-cpp-tutorial/issues) to report the `typo` error.
|
||||||
|
|
||||||
- Do not hasitate to submit a principle error because it prevent wrong knowledge being propagation.
|
- Do not hesitate to submit a `principle` error because it will prevent wrong knowledge being spread.
|
||||||
Report the error immediately by [submitting issue](https://github.com/changkun/modern-cpp-tutorial/issues) to avoid the spread of wrong knowledge.
|
Report the error immediately by [submitting issue](https://github.com/changkun/modern-cpp-tutorial/issues) to avoid the propogation of wrong knowledge.
|
||||||
|
|
||||||
- If you found some part of the book is confusing, you are very welcome to [submit an issue](https://github.com/changkun/modern-cpp-tutorial/issues) for asking questions.
|
- If you found some part of the book confusing, you are very welcome to [submit an issue](https://github.com/changkun/modern-cpp-tutorial/issues) for asking questions.
|
||||||
|
|
||||||
- The book cannot cover the entire C++ of course, however, you are very welcome to [submit an issue](https://github.com/changkun/modern-cpp-tutorial/issues) for a suggestion if you found some important feature is missing in the book.
|
- The book cannot cover the entirety C++ of course, however, you are very welcome to [submit an issue](https://github.com/changkun/modern-cpp-tutorial/issues) with a suggestion if you find some important feature is missing in the book.
|
||||||
|
|
||||||
## Pull Request
|
## Pull Request
|
||||||
|
|
||||||
"C++ 11/14/17 On the Fly" is open source so that everyone can contribute to contribute to PR. However, it is required read the following instructions carefully before submitting your pull request:
|
"C++ 11/14/17 On the Fly" is open source so that everyone can contribute to contribute via a PR. However, it is required to read the following instructions carefully before submitting your pull request:
|
||||||
|
|
||||||
- Before you submit your pull request, make sure that the [issue list](https://github.com/changkun/modern-cpp-tutorial/issues) already contains the problem you want to solve. If not, please refer to the **Submit Issue** section.
|
- Before you submit your pull request, make sure that the [issue list](https://github.com/changkun/modern-cpp-tutorial/issues) already contains the problem you want to solve. If not, please refer to the **Submit Issue** section.
|
||||||
|
|
||||||
- Make sure your PR has improved the `typo` error of more than 50 words, otherwise please do not submit a PR.
|
- Make sure your PR has improved more than 50 `typo` errors, otherwise please do not submit a PR.
|
||||||
|
|
||||||
- For a PR that fixes principled errors, please don't hesitate, all readers of the book are very grateful for your contribution!
|
- For a PR that fixes principled errors, please don't hesitate, all of the readers of the book are very grateful for your contribution!
|
||||||
|
|
||||||
- If you would like to be a co-author of this book, please send an email to ask: `hi at changkun dot us`.
|
- If you would like to be a co-author of this book, please send an email to ask: `hi at changkun dot us`.
|
||||||
|
|
||||||
Since this repository provides a variety of reading approaches, thus make sure you have checked all items in the following checklist:
|
Since this repository provides a variety of reading approaches, thus make sure you have checked all items in the following checklist:
|
||||||
|
|
||||||
- [ ] If you only making changes to the main part of the book (i.e. the `book` folder), and no changes to the code snippet, then you are good to go;
|
- [ ] If you only making changes to the main part of the book (i.e. the `book` folder), and no changes to the code snippet, then you are good to go;
|
||||||
- [ ] If you also change the code snippet in the main body of the book, then you need to synchronize the corresponding code snippet in the `code` folder;
|
- [ ] If you also changed the code snippet in the main body of the book, then you need to synchronize the corresponding code snippet in the `code` folder;
|
||||||
- [ ] If you changes also involve the exercises, you also need to synchronize the contents of the `exercises` folder.
|
- [ ] If your changes also involve the exercises, you also need to synchronize the contents of the `exercises` folder.
|
||||||
|
|
||||||
# 如何参与贡献
|
# 如何参与贡献
|
||||||
|
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016 - 2019 Changkun Ou <hi@changkun.us>
|
Copyright (c) 2016 - 2020 Changkun Ou <hi@changkun.de>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
19
Makefile
19
Makefile
@@ -1,22 +1,25 @@
|
|||||||
DOCKER_ENV=changkun/modern-cpp-tutorial:build-env
|
NAME=modern-cpp-tutorial
|
||||||
|
DOCKER_ENV=changkun/$(NAME):build-env
|
||||||
|
TARGET = pdf epub
|
||||||
LANGS = zh-cn en-us
|
LANGS = zh-cn en-us
|
||||||
ALL_BUILDS = website pdf
|
ALL_BUILDS = website $(TARGET)
|
||||||
|
|
||||||
# dep
|
# dep
|
||||||
|
|
||||||
all: $(ALL_BUILDS)
|
all: $(ALL_BUILDS)
|
||||||
|
|
||||||
pdf: $(LANGS)
|
$(TARGET): $(LANGS)
|
||||||
$(LANGS):
|
mkdir -p website/public/modern-cpp/$@/
|
||||||
cd pdf/$@ && make && make clean
|
for lang in $^ ; do \
|
||||||
mkdir -p website/public/modern-cpp/pdf
|
cd $@/$${lang} && make && make clean && cd ../..; \
|
||||||
mv pdf/$@/modern-cpp-tutorial.pdf website/public/modern-cpp/pdf/modern-cpp-tutorial-$@.pdf
|
mv $@/$${lang}/$(NAME).$@ website/public/modern-cpp/$@/$(NAME)-$${lang}.$@; \
|
||||||
|
done
|
||||||
|
|
||||||
website:
|
website:
|
||||||
cd website && make
|
cd website && make
|
||||||
|
|
||||||
build:
|
build:
|
||||||
docker run --rm -v `pwd`:/modern-cpp-tutorial -it $(DOCKER_ENV) make
|
docker run --rm -v `pwd`:/$(NAME) $(DOCKER_ENV) make
|
||||||
|
|
||||||
# dev
|
# dev
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# 现代 C++ 教程:高速上手 C++11/14/17/20
|
# 现代 C++ 教程:高速上手 C++11/14/17/20
|
||||||
|
|
||||||
[](./README.md) [](./README-zh-cn.md) [](./assets/donate.md) [](./assets/community.md)
|
 [](./README.md) [](./README-zh-cn.md) [](./assets/donate.md)
|
||||||
|
|
||||||
## 本书目的
|
## 本书目的
|
||||||
|
|
||||||
@@ -22,8 +22,9 @@
|
|||||||
你可以选择以下几种阅读方式:
|
你可以选择以下几种阅读方式:
|
||||||
|
|
||||||
1. [GitHub 在线](./book/zh-cn/toc.md)
|
1. [GitHub 在线](./book/zh-cn/toc.md)
|
||||||
2. [PDF 文档](https://changkun.de/modern-cpp/modern-cpp-tutorial-zh-cn.pdf)
|
2. [PDF 文档](https://changkun.de/modern-cpp/pdf/modern-cpp-tutorial-zh-cn.pdf)
|
||||||
3. [网站](https://changkun.de/modern-cpp/)
|
3. [EPUB 文档](https://changkun.de/modern-cpp/epub/modern-cpp-tutorial-zh-cn.epub)
|
||||||
|
4. [网站](https://changkun.de/modern-cpp/)
|
||||||
|
|
||||||
## 相关代码
|
## 相关代码
|
||||||
|
|
||||||
@@ -49,8 +50,15 @@ $ make build
|
|||||||
|
|
||||||
笔者时间和水平有限,如果读者发现书中内容的错误,欢迎提 [Issue](https://github.com/changkun/modern-cpp-tutorial/issues),或者直接提 [Pull request](https://github.com/changkun/modern-cpp-tutorial/pulls)。详细贡献指南请参考[如何参与贡献](CONTRIBUTING.md),由衷感谢每一位指出本书中出现错误的读者,包括但不限于 [Contributors](https://github.com/changkun/modern-cpp-tutorial/graphs/contributors)。
|
笔者时间和水平有限,如果读者发现书中内容的错误,欢迎提 [Issue](https://github.com/changkun/modern-cpp-tutorial/issues),或者直接提 [Pull request](https://github.com/changkun/modern-cpp-tutorial/pulls)。详细贡献指南请参考[如何参与贡献](CONTRIBUTING.md),由衷感谢每一位指出本书中出现错误的读者,包括但不限于 [Contributors](https://github.com/changkun/modern-cpp-tutorial/graphs/contributors)。
|
||||||
|
|
||||||
|
<p>本项目还由以下产品提供赞助支持:</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://www.digitalocean.com/?refcode=834a3bbc951b&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=CopyPaste">
|
||||||
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](./LICENSE)。
|
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](./LICENSE)。
|
||||||
|
|||||||
37
README.md
37
README.md
@@ -2,23 +2,22 @@
|
|||||||
|
|
||||||
# Modern C++ Tutorial: C++11/14/17/20 On the Fly
|
# Modern C++ Tutorial: C++11/14/17/20 On the Fly
|
||||||
|
|
||||||
[](./README.md) [](./README-zh-cn.md) [](./assets/donate.md) [](./assets/community.md)
|
 [](./README.md) [](./README-zh-cn.md) [](./assets/donate.md)
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
The book claims "On the Fly". Its intent is to provide a comprehensive introduction to the relevant features regarding modern C++ (before 2020s).
|
The book claims to be "On the Fly". Its intent is to provide a comprehensive introduction to the relevant features regarding modern C++ (before 2020s).
|
||||||
Readers can choose interesting content according to the following table of content to learn and quickly familiarize the new features you would like to learn.
|
Readers can choose interesting content according to the following table of content to learn and quickly familiarize the new features you would like to learn.
|
||||||
Readers should aware that all of these features are not required. It should be leart when you really need it.
|
Readers should be aware that not all of these features are required. Instead, it should be learned when you really need it.
|
||||||
|
|
||||||
At the same time, instead of grammar-only, the book introduces the historical background as simple as possible of its technical requirements, which provides great help in understanding why these features comes out.
|
At the same time, instead of coding-only, the book introduces the historical background of its technical requirements (as simple as possible), which provides great help in understanding why these features came out.
|
||||||
|
|
||||||
In addition, The author would like to encourage that readers should be able to use modern C++ directly in their new projects and migrate their old projects to modern C++ gradually after read the book.
|
In addition, the author would like to encourage readers to use modern C++ directly in their new projects and migrate their old projects to modern C++ gradually after reading the book.
|
||||||
|
|
||||||
## Targets
|
## Targets
|
||||||
|
|
||||||
- This book assumes that readers are already familiar with traditional C++ (i.e. C++98 or earlier), at least they do not have any difficulty in reading traditional C++ code. In other words, those who have long experience in traditional C++ and people who desire to quickly understand the features of modern C++ in a short period of time are well suited to read the book;
|
- This book assumes that readers are already familiar with traditional C++ (i.e. C++98 or earlier), or at least that they do not have any difficulty in reading traditional C++ code. In other words, those who have long experience in traditional C++ and people who desire to quickly understand the features of modern C++ in a short period of time are well suited to read the book.
|
||||||
|
|
||||||
- This book introduces to a certain extent of the dark magic of modern C++. However, these magics are very limited, they are not suitable for readers who want to learn advanced C++. The purpose of this book is offering a quick start for modern C++. Of course, advanced readers can also use this book to review and examine themselves on modern C++.
|
- This book introduces, to a certain extent, the dark magic of modern C++. However, these magic tricks are very limited, they are not suitable for readers who want to learn advanced C++. The purpose of this book is offering a quick start for modern C++. Of course, advanced readers can also use this book to review and examine themselves on modern C++.
|
||||||
|
|
||||||
## Start
|
## Start
|
||||||
|
|
||||||
@@ -26,23 +25,24 @@ You can choose from the following reading methods:
|
|||||||
|
|
||||||
- [GitHub Online](./book/en-us/toc.md)
|
- [GitHub Online](./book/en-us/toc.md)
|
||||||
- [PDF document](https://changkun.de/modern-cpp/pdf/modern-cpp-tutorial-en-us.pdf)
|
- [PDF document](https://changkun.de/modern-cpp/pdf/modern-cpp-tutorial-en-us.pdf)
|
||||||
|
- [EPUB document](https://changkun.de/modern-cpp/epub/modern-cpp-tutorial-en-us.epub)
|
||||||
- [Website](https://changkun.de/modern-cpp)
|
- [Website](https://changkun.de/modern-cpp)
|
||||||
|
|
||||||
## Code
|
## Code
|
||||||
|
|
||||||
Each chapter of this book has a lot of code. If you encounter problems when writing your own code with the introductory features of the book, you might as well read the source code attached to the book. You can find the book [here](./code). All the code organized by chapter, the folder name is the chapter number.
|
Each chapter of this book contains a lot of code. If you encounter problems while writing your own code with the introductory features of the book, reading the source code attached to the book might be of help. You can find the book [here](./code). All the code is organized by chapter, the folder name is the chapter number.
|
||||||
|
|
||||||
## Exercises
|
## Exercises
|
||||||
|
|
||||||
There are few exercises At the end of each chapter of the book. It is for testing whether you can use the knowledge points in the current chapter. You can find the possible answer to the problem from [here](./exercises). The folder name is the chapter number.
|
There are few exercises at the end of each chapter of the book. These are meant to test whether you have mastered the knowledge in the current chapter. You can find the possible answer to the problem [here](./exercises). Again, the folder name is the chapter number.
|
||||||
|
|
||||||
## Website
|
## Website
|
||||||
|
|
||||||
The source code of the [website](https://changkun.de/modern-cpp) of this book can be found [here](./website), which is built by [hexo](https://hexo.io) and [vuejs](https://vuejs.org). The website provides you another way of reading the book, it is also adapts to mobile.
|
The source code of the [website](https://changkun.de/modern-cpp) of this book can be found [here](./website), which is built by [hexo](https://hexo.io) and [vuejs](https://vuejs.org). The website provides you another way of reading the book, it also adapts to mobile browsers.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
If you are interested in build everything locally, it is recommended using [Docker](https://docs.docker.com/install/). To build, simply run:
|
If you are interested in building everything locally, it is recommended using [Docker](https://docs.docker.com/install/). To build, simply run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ make build
|
$ make build
|
||||||
@@ -52,10 +52,17 @@ $ make build
|
|||||||
|
|
||||||
This book was originally written in Chinese by [Changkun Ou](https://changkun.de).
|
This book was originally written in Chinese by [Changkun Ou](https://changkun.de).
|
||||||
|
|
||||||
The author has limited time and language skills. If readers find any mistakes of the book or any language improvements, please feel free to open an [Issue](https://github.com/changkun/modern-cpp-tutorial/issues) or start a [Pull request](https://github.com/changkun/modern-cpp-tutorial/pulls). For detailed guidelines and checklist, please refer to [How to contribute](CONTRIBUTING.md).
|
The author has limited time and language skills. If readers find any mistakes in the book or any language improvements, please feel free to open an [Issue](https://github.com/changkun/modern-cpp-tutorial/issues) or start a [Pull request](https://github.com/changkun/modern-cpp-tutorial/pulls). For detailed guidelines and checklist, please refer to [How to contribute](CONTRIBUTING.md).
|
||||||
|
|
||||||
The author would be grateful to all contributors, including but not limited to [Contributors](https://github.com/changkun/modern-cpp-tutorial/graphs/contributors).
|
The author is grateful to all contributors, including but not limited to [Contributors](https://github.com/changkun/modern-cpp-tutorial/graphs/contributors).
|
||||||
|
|
||||||
|
<p>This project is also supported by:</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://www.digitalocean.com/?refcode=834a3bbc951b&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=CopyPaste">
|
||||||
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](./LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](./LICENSE).
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
title: 社区
|
|
||||||
type: about
|
|
||||||
order: 2
|
|
||||||
---
|
|
||||||
|
|
||||||
## Community
|
|
||||||
|
|
||||||
The book offers a telegram chat group, feel free to join if you are interested:
|
|
||||||
|
|
||||||
[](https://t.me/joinchat/FEeulBM5OVYzuDI4phQ9Mg)
|
|
||||||
|
|
||||||
## 交流
|
|
||||||
|
|
||||||
本书有以下读者 QQ 交流群,有兴趣的读者可以加入,加群需正确回答加群密码:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> 提示: `bW9kZXJuLWNwcC10dXRvcmlhbC1naXRodWI=`
|
|
||||||
BIN
assets/cover-2nd-en-logo.png
Normal file
BIN
assets/cover-2nd-en-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
assets/cover-2nd-logo.png
Normal file
BIN
assets/cover-2nd-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
assets/figures/pointers1_en.png
Normal file
BIN
assets/figures/pointers1_en.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
@@ -10,36 +10,36 @@ order: 0
|
|||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
C++ user group is a fairly large. From the advent of C++98 to the official finalization of C++11, it has accumulated over a decade. C++14/17 is an important complement and optimization for C++11, and C++20 brings this language to the door of modernization. The extended features of all these new standards are given to the C++ language. Infused with new vitality.
|
The C++ programming language owns a fairly large user group. From the advent of C++98 to the official finalization of C++11, it has continued to stay relevant. C++14/17 is an important complement and optimization for C++11, and C++20 brings this language to the door of modernization. The extended features of all these new standards are integrated into the C++ language and infuse it with new vitality.
|
||||||
C++ programmers, who are still using **traditional C++** (this book refers to C++98 and its previous C++ standards as traditional C++), may even amzed by the fact that they are not using the same language while reading modern C++ code.
|
C++ programmers who are still using **traditional C++** (this book refers to C++98 and its previous standards as traditional C++) may even amazed by the fact that they are not using the same language while reading modern C++ code.
|
||||||
|
|
||||||
**Modern C++** (this book refers to C++11/14/17/20) introduces a lot of features into traditional C++, which makes the whole C++ become language that modernized. Modern C++ not only enhances the usability of the C++ language itself, but the modification of the `auto` keyword semantics gives us more confidence in manipulating extremely complex template types. At the same time, a lot of enhancements have been made to the language runtime. The emergence of Lambda expressions has made C++ have the "closure" feature of "anonymous functions", which is almost in modern programming languages (such as Python/Swift/.. It has become commonplace, and the emergence of rvalue references has solved the problem of temporary object efficiency that C++ has long been criticized.
|
**Modern C++** (this book refers to C++11/14/17/20) introduces many features into traditional C++ which bring the entire language to a new level of modernization. Modern C++ not only enhances the usability of the C++ language itself, but the modification of the `auto` keyword semantics gives us more confidence in manipulating extremely complex template types. At the same time, a lot of enhancements have been made to the language runtime. The emergence of Lambda expressions has given C++ the "closure" feature of "anonymous functions", which are in almost all modern programming languages (such as Python, Swift, etc). It has become commonplace, and the emergence of rvalue references has solved the problem of temporary object efficiency that C++ has long been criticized for.
|
||||||
|
|
||||||
C++17 is the direction that has been promoted by the C++ community in the past three years. It also points out an important development direction of **modern C++** programming. Although it does not appear as much as C++11, it contains a large number of small and beautiful languages and features (such as structured binding), and the appearance of these features once again corrects our programming paradigm in C++.
|
C++17 is the direction that has been promoted by the C++ community in the past three years. It also points out an important development direction of **modern C++** programming. Although it does not appear as much as C++11, it contains a large number of small and beautiful languages and features (such as structured binding), and the appearance of these features once again corrects our programming paradigm in C++.
|
||||||
|
|
||||||
Modern C++ also adds a lot of tools and methods to its own standard library, such as `std::thread` at the level of the language itself, which supports concurrent programming and no longer depends on the underlying system on different platforms. The API implements cross-platform support at the language level; `std::regex` provides full regular expression support and more. C++98 has been proven to be a very successful "paradigm", and the emergence of modern C++ further promotes this paradigm, making C++ a better language for system programming and library development. Concepts provide verification on the compile-time of template parameters, further enhancing the usability of the language.
|
Modern C++ also adds a lot of tools and methods to its standard library such as `std::thread` at the level of the language itself, which supports concurrent programming and no longer depends on the underlying system on different platforms. The API implements cross-platform support at the language level; `std::regex` provides full regular expression support and more. C++98 has been proven to be a very successful "paradigm", and the emergence of modern C++ further promotes this paradigm, making C++ a better language for system programming and library development. Concepts verify the compile-time of template parameters, further enhancing the usability of the language.
|
||||||
|
|
||||||
In conclusion, as an advocate and practitioner of C++, we always maintain an open mind to accept new things, and we can promote the development of C++ faster, making this old and novel language more vibrant.
|
In conclusion, as an advocate and practitioner of C++, we always maintain an open mind to accept new things, and we can promote the development of C++ faster, making this old and novel language more vibrant.
|
||||||
|
|
||||||
## Targets
|
## Targets
|
||||||
|
|
||||||
- This book assumes that readers are already familiar with traditional C++ (i.e. C++98 or earlier), at least they do not have any difficulty in reading traditional C++ code. In other words, those who have long experience in traditional C++ and people who desire to quickly understand the features of modern C++ in a short period of time are well suited to read the book;
|
- This book assumes that readers are already familiar with traditional C++ (i.e. C++98 or earlier), at least they do not have any difficulty in reading traditional C++ code. In other words, those who have long experience in traditional C++ and people who desire to quickly understand the features of modern C++ in a short period are well suited to read the book;
|
||||||
|
|
||||||
- This book introduces to a certain extent of the dark magic of modern C++. However, these magics are very limited, they are not suitable for readers who want to learn advanced C++. The purpose of this book is offering a quick start for modern C++. Of course, advanced readers can also use this book to review and examine themselves on modern C++.
|
- This book introduces to a certain extent of the dark magic of modern C++. However, these magics are very limited, they are not suitable for readers who want to learn advanced C++. The purpose of this book is to offer a quick start for modern C++. Of course, advanced readers can also use this book to review and examine themselves on modern C++.
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
The book claims "On the Fly". Its intent is to provide a comprehensive introduction to the relevant features regarding modern C++ (before 2020s).
|
The book claims "On the Fly". It intends to provide a comprehensive introduction to the relevant features regarding modern C++ (before the 2020s).
|
||||||
Readers can choose interesting content according to the following table of content to learn and quickly familiarize the new features you would like to learn.
|
Readers can choose interesting content according to the following table of contents to learn and quickly familiarize themselves with the new features that are available.
|
||||||
Readers should aware that all of these features are not required. It should be leart when you really need it.
|
Readers should aware that all of these features are not required. It should be learned when you need it.
|
||||||
|
|
||||||
At the same time, instead of grammar-only, the book introduces the historical background as simple as possible of its technical requirements, which provides great help in understanding why these features comes out.
|
At the same time, instead of grammar-only, the book introduces the historical background as simple as possible of its technical requirements, which provides great help in understanding why these features come out.
|
||||||
|
|
||||||
In addition, The author would like to encourage that readers should be able to use modern C++ directly in their new projects and migrate their old projects to modern C++ gradually after read the book.
|
Also, the author would like to encourage that readers should be able to use modern C++ directly in their new projects and migrate their old projects to modern C++ gradually after reading the book.
|
||||||
|
|
||||||
## Code
|
## Code
|
||||||
|
|
||||||
Each chapter of this book has a lot of code. If you encounter problems when writing your own code with the introductory features of the book, you might as well read the source code attached to the book. You can find the book [here](../../code). All the code organized by chapter, the folder name is the chapter number.
|
Each chapter of this book has a lot of code. If you encounter problems when writing your own code with the introductory features of the book, you might as well read the source code attached to the book. You can find the book [here](../../code). All the code is organized by chapter, the folder name is the chapter number.
|
||||||
|
|
||||||
## Exercises
|
## Exercises
|
||||||
|
|
||||||
@@ -49,4 +49,4 @@ There are few exercises At the end of each chapter of the book. It is for testin
|
|||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ order: 1
|
|||||||
|
|
||||||
[TOC]
|
[TOC]
|
||||||
|
|
||||||
**Compilation Environment**: This book will use `clang++` as the only compiler used,
|
**Compilation Environment**: This book will use `clang++` as the only compiler used,
|
||||||
and always use the `-std=c++2a` compilation flag in your code.
|
and always use the `-std=c++2a` compilation flag in your code.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -21,14 +21,15 @@ InstalledDir: /Library/Developer/CommandLineTools/usr/bin
|
|||||||
|
|
||||||
## 1.1 Deprecated Features
|
## 1.1 Deprecated Features
|
||||||
|
|
||||||
Before learning modern C++, let's take a look at the main features that have been deprecated since C++11:
|
Before learning modern C++, let's take a look at the main features that have deprecated since C++11:
|
||||||
|
|
||||||
> **Note**: Deprecation is not completely unusable, it is only intended to imply that programmers will disappear from future standards and should be avoided. However, the deprecated features are still part of the standard library, and most of the features are actually "permanently" reserved for compatibility reasons.
|
> **Note**: Deprecation is not completely unusable, it is only intended to imply that features will disappear from future standards and should be avoided. But, the deprecated features are still part of the standard library, and most of the features are actually "permanently" reserved for compatibility reasons.
|
||||||
|
|
||||||
- **The string literal constant is no longer allowed to be assigned to a `char *`. If you need to assign and initialize a `char *` with a string literal constant, you should use `const char *` or `auto`.**
|
- **The string literal constant is no longer allowed to be assigned to a `char *`. If you need to assign and initialize a `char *` with a string literal constant, you should use `const char *` or `auto`.**
|
||||||
```cpp
|
|
||||||
char *str = "hello world!"; // A deprecation warning will appear
|
```cpp
|
||||||
```
|
char *str = "hello world!"; // A deprecation warning will appear
|
||||||
|
```
|
||||||
|
|
||||||
- **C++98 exception description, `unexpected_handler`, `set_unexpected()` and other related features are deprecated and should use `noexcept`.**
|
- **C++98 exception description, `unexpected_handler`, `set_unexpected()` and other related features are deprecated and should use `noexcept`.**
|
||||||
|
|
||||||
@@ -42,19 +43,19 @@ Before learning modern C++, let's take a look at the main features that have bee
|
|||||||
|
|
||||||
- **C language style type conversion is deprecated (ie using `(convert_type)`) before variables, and `static_cast`, `reinterpret_cast`, `const_cast` should be used for type conversion.**
|
- **C language style type conversion is deprecated (ie using `(convert_type)`) before variables, and `static_cast`, `reinterpret_cast`, `const_cast` should be used for type conversion.**
|
||||||
|
|
||||||
- **In particular, some of the C standard libraries that can be used are deprecated in the latest C++17 standard, such as `<ccomplex>`, `<cstdalign>`, `<cstdbool>` and `<ctgmath>` Wait**
|
- **In particular, some of the C standard libraries that can be used are deprecated in the latest C++17 standard, such as `<ccomplex>`, `<cstdalign>`, `<cstdbool>` and `<ctgmath>` etc.**
|
||||||
|
|
||||||
- ... and many more
|
- ... and many more
|
||||||
|
|
||||||
There are also other features such as parameter binding (C++11 provides `std::bind` and `std::function`), `export`, and etc. are also deprecated. These features mentioned above **If you have never used or heard of it, please don't try to understand them. You should move closer to the new standard and learn new features directly**. After all, technology is moving forward.
|
There are also other features such as parameter binding (C++11 provides `std::bind` and `std::function`), `export` etc. are also deprecated. These features mentioned above **If you have never used or heard of it, please don't try to understand them. You should move closer to the new standard and learn new features directly**. After all, technology is moving forward.
|
||||||
|
|
||||||
## 1.2 Compatibilities with C
|
## 1.2 Compatibilities with C
|
||||||
|
|
||||||
For some force majeure and historical reasons, we had to use some C code (even old C code) in C++, for example, Linux system calls. Before the advent of modern C++, most people talked about "what is the difference between C and C++". Generally speaking, in addition to answering the object-oriented class features and the template features of generic programming, there is no other opinion, or even a direct answer. "Almost" is also a lot of people. The Wayne diagram in Figure 1.2 roughly answers the C and C++ related compatibility.
|
For some force majeure and historical reasons, we had to use some C code (even old C code) in C++, for example, Linux system calls. Before the advent of modern C++, most people talked about "what is the difference between C and C++". Generally speaking, in addition to answering the object-oriented class features and the template features of generic programming, there is no other opinion or even a direct answer. "Almost" is also a lot of people. The Venn diagram in Figure 1.2 roughly answers the C and C++ related compatibility.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
From now on, you should have the idea that "C++ is **not** a superset of C" in your mind (and not from the beginning, later [References for further reading] (# further reading references) The difference between C++98 and C99 is given). When writing C++, you should also avoid using program styles such as `void*` whenever possible. When you have to use C, you should pay attention to the use of `extern "C"`, separate the C language code from the C++ code, and then unify the link, for instance:
|
From now on, you should have the idea that "C++ is **not** a superset of C" in your mind (and not from the beginning, later [References for further reading](#further-readings) The difference between C++98 and C99 is given). When writing C++, you should also avoid using program styles such as `void*` whenever possible. When you have to use C, you should pay attention to the use of `extern "C"`, separate the C language code from the C++ code, and then unify the link, for instance:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// foo.h
|
// foo.h
|
||||||
@@ -92,7 +93,7 @@ You should first compile the C code with `gcc`:
|
|||||||
gcc -c foo.c
|
gcc -c foo.c
|
||||||
```
|
```
|
||||||
|
|
||||||
Comple and output the `foo.o` file, and link the C++ code to the `.o` file using `clang++` (or both compile to `.o` and then unlink them together):
|
Compile and output the `foo.o` file, and link the C++ code to the `.o` file using `clang++` (or both compile to `.o` and then link them together):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
clang++ 1.1.cpp foo.o -std=c++2a -o 1.1
|
clang++ 1.1.cpp foo.o -std=c++2a -o 1.1
|
||||||
@@ -115,11 +116,12 @@ LDFLAGS_COMMON = -std=c++2a
|
|||||||
all:
|
all:
|
||||||
$(C) -c $(SOURCE_C)
|
$(C) -c $(SOURCE_C)
|
||||||
$(CXX) $(SOURCE_CXX) $(OBJECTS_C) $(LDFLAGS_COMMON) -o $(TARGET)
|
$(CXX) $(SOURCE_CXX) $(OBJECTS_C) $(LDFLAGS_COMMON) -o $(TARGET)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.o $(TARGET)
|
rm -rf *.o $(TARGET)
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note: Indentation in `Makefile` is a tab instead of a space character. If you copy this code directly into your editor, the tab may be automatically replaced. Please ensure the indentation in the `Makefile`. It is done by tabs.
|
> **Note**: Indentation in `Makefile` is a tab instead of a space character. If you copy this code directly into your editor, the tab may be automatically replaced. Please ensure the indentation in the `Makefile` is done by tabs.
|
||||||
>
|
>
|
||||||
> If you don't know the use of `Makefile`, it doesn't matter. In this tutorial, you won't build code that is written too complicated. You can also read this book by simply using `clang++ -std=c++2a` on the command line.
|
> If you don't know the use of `Makefile`, it doesn't matter. In this tutorial, you won't build code that is written too complicated. You can also read this book by simply using `clang++ -std=c++2a` on the command line.
|
||||||
|
|
||||||
@@ -138,10 +140,10 @@ Don't worry at the moment, we will come to meet them in our later chapters.
|
|||||||
## Further Readings
|
## Further Readings
|
||||||
|
|
||||||
- [A Tour of C++ (2nd Edition) Bjarne Stroustrup](https://www.amazon.com/dp/0134997832/ref=cm_sw_em_r_mt_dp_U_GogjDbHE2H53B)
|
- [A Tour of C++ (2nd Edition) Bjarne Stroustrup](https://www.amazon.com/dp/0134997832/ref=cm_sw_em_r_mt_dp_U_GogjDbHE2H53B)
|
||||||
- [C++ History](http://en.cppreference.com/w/cpp/language/history)
|
[History of C++](http://en.cppreference.com/w/cpp/language/history)
|
||||||
- [C++ compiler support](https://en.cppreference.com/w/cpp/compiler_support)
|
- [C++ compiler support](https://en.cppreference.com/w/cpp/compiler_support)
|
||||||
- [Incompatibilities Between ISO C and ISO C++](http://david.tribble.com/text/cdiffs.htm#C99-vs-CPP98)
|
- [Incompatibilities Between ISO C and ISO C++](http://david.tribble.com/text/cdiffs.htm#C99-vs-CPP98)
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -8,30 +8,31 @@ order: 2
|
|||||||
|
|
||||||
[TOC]
|
[TOC]
|
||||||
|
|
||||||
When we declare, define a variable or constant, and control the flow of code,
|
When we declare, define a variable or constant, and control the flow of code,
|
||||||
object-oriented functions, template programming, etc., before the runtime,
|
object-oriented functions, template programming, etc., before the runtime,
|
||||||
it may happen when writing code or compiler compiling code.
|
it may happen when writing code or compiler compiling code.
|
||||||
To this end, we usually talk about **language usability**,
|
To this end, we usually talk about **language usability**,
|
||||||
which refers to the language behavior that occurred before the runtime.
|
which refers to the language behavior that occurred before the runtime.
|
||||||
|
|
||||||
## 2.1 Constants
|
## 2.1 Constants
|
||||||
|
|
||||||
### nullptr
|
### nullptr
|
||||||
|
|
||||||
The purpose of `nullptr` appears to replace `NULL`. In a sense,
|
The purpose of `nullptr` appears to replace `NULL`. There are **null pointer constants** in the C and C++ languages,
|
||||||
traditional C++ treats `NULL` and `0` as the same thing,
|
which can be implicitly converted to null pointer value of any pointer type,
|
||||||
depending on how the compiler defines NULL,
|
or null member pointer value of any pointer-to-member type in C++.
|
||||||
and some compilers define NULL as `((void*)0)` Some will define it directly as `0`.
|
`NULL` is provided by the standard library implementation and defined as an implementation-defined null pointer constant.
|
||||||
|
In C, some standard libraries defines `NULL` as `((void*)0)` and some define it as `0`.
|
||||||
|
|
||||||
C++ ** does not allow ** to implicitly convert `void *` to other types.
|
C++ **does not allow** to implicitly convert `void *` to other types, and thus `((void*)0)` is not a valid implementation
|
||||||
But if the compiler tries to define `NULL` as `((void*)0)`, then in the following code:
|
of `NULL`. If the standard library tries to define `NULL` as `((void*)0)`, then compilation error would occur in the following code:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
char *ch = NULL;
|
char *ch = NULL;
|
||||||
```
|
```
|
||||||
|
|
||||||
C++ without the `void *` implicit conversion has to define `NULL` as `0`.
|
C++ without the `void *` implicit conversion has to define `NULL` as `0`.
|
||||||
This still creates a new problem. Defining `NULL` to 0 will cause the overloading feature in `C++` to be confusing.
|
This still creates a new problem. Defining `NULL` to `0` will cause the overloading feature in `C++` to be confusing.
|
||||||
Consider the following two `foo` functions:
|
Consider the following two `foo` functions:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -41,7 +42,7 @@ void foo(int);
|
|||||||
|
|
||||||
Then the `foo(NULL);` statement will call `foo(int)`, which will cause the code to be counterintuitive.
|
Then the `foo(NULL);` statement will call `foo(int)`, which will cause the code to be counterintuitive.
|
||||||
|
|
||||||
To solve this problem, C++11 introduced the `nullptr` keyword, which is specifically used to distinguish null pointers, 0. The type of `nullptr` is `nullptr_t`, which can be implicitly converted to any pointer or member pointer type, and can be compared equally or unequally with them.
|
To solve this problem, C++11 introduced the `nullptr` keyword, which is specifically used to distinguish null pointers, `0`. The type of `nullptr` is `nullptr_t`, which can be implicitly converted to any pointer or member pointer type, and can be compared equally or unequally with them.
|
||||||
|
|
||||||
You can try to compile the following code using clang++:
|
You can try to compile the following code using clang++:
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ int main() {
|
|||||||
std::cout << "NULL == nullptr" << std::endl;
|
std::cout << "NULL == nullptr" << std::endl;
|
||||||
|
|
||||||
foo(0); // will call foo(int)
|
foo(0); // will call foo(int)
|
||||||
// foo(NULL); // doen't compile
|
// foo(NULL); // doesn't compile
|
||||||
foo(nullptr); // will call foo(char*)
|
foo(nullptr); // will call foo(char*)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -81,24 +82,22 @@ foo(int) is called
|
|||||||
foo(char*) is called
|
foo(char*) is called
|
||||||
```
|
```
|
||||||
|
|
||||||
From the output we can see that `NULL` is different from `0` and `nullptr`.
|
From the output we can see that `NULL` is different from `0` and `nullptr`.
|
||||||
So, develop the habit of using `nullptr` directly.
|
So, develop the habit of using `nullptr` directly.
|
||||||
|
|
||||||
In addition, in the above code, we used `decltype` and
|
In addition, in the above code, we used `decltype` and
|
||||||
`std::is_same` which are modern C++ syntax.
|
`std::is_same` which are modern C++ syntax.
|
||||||
In simple terms, `decltype` is used for type derivation,
|
In simple terms, `decltype` is used for type derivation,
|
||||||
and `std::is_same` is used.
|
and `std::is_same` is used to compare the equality of the two types.
|
||||||
To compare the equality of the two types,
|
We will discuss them in detail later in the [decltype](#decltype) section.
|
||||||
we will discuss them in detail later in the [decltype](#decltype) section.
|
|
||||||
|
|
||||||
### constexpr
|
### constexpr
|
||||||
|
|
||||||
C++ itself already has the concept of constant expressions,
|
C++ itself already has the concept of constant expressions, such as 1+2,
|
||||||
such as 1+2, 3*4. Such expressions always produce the same result
|
3\*4. Such expressions always produce the same result without any side effects.
|
||||||
without any side effects. If the compiler can directly optimize
|
If the compiler can directly optimize and embed these expressions into the program at
|
||||||
and embed these expressions into the program at compile time,
|
compile-time, it will increase the performance of the program. A very obvious example
|
||||||
it will increase the performance of the program.
|
is in the definition phase of an array:
|
||||||
A very obvious example is in the definition phase of an array:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -131,7 +130,7 @@ int main() {
|
|||||||
|
|
||||||
// char arr_5[len_foo()+5]; // illegal
|
// char arr_5[len_foo()+5]; // illegal
|
||||||
char arr_6[len_foo_constexpr() + 1]; // legal
|
char arr_6[len_foo_constexpr() + 1]; // legal
|
||||||
|
|
||||||
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
|
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
|
||||||
std::cout << fibonacci(10) << std::endl;
|
std::cout << fibonacci(10) << std::endl;
|
||||||
|
|
||||||
@@ -139,23 +138,23 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above example, `char arr_4[len_2]` may be confusing because `len_2` has been defined as a constant.
|
In the above example, `char arr_4[len_2]` may be confusing because `len_2` has been defined as a constant.
|
||||||
Why is `char arr_4[len_2]` still illegal?
|
Why is `char arr_4[len_2]` still illegal?
|
||||||
This is because the length of the array in the C++ standard must be a constant expression,
|
This is because the length of the array in the C++ standard must be a constant expression,
|
||||||
and for `len_2`, this is a `const` constant, not a constant expression,
|
and for `len_2`, this is a `const` constant, not a constant expression,
|
||||||
so even if this behavior is in most compilers Both support, but) it is an illegal behavior,
|
so even if this behavior is supported by most compilers, but it is an illegal behavior,
|
||||||
we need to use the `constexpr` feature introduced in C++11, which will be introduced next,
|
we need to use the `constexpr` feature introduced in C++11, which will be introduced next,
|
||||||
to solve this problem; for `arr_5`, before C++98 The compiler cannot know that `len_foo()`
|
to solve this problem; for `arr_5`, before C++98 The compiler cannot know that `len_foo()`
|
||||||
actually returns a constant at runtime, which causes illegal production.
|
actually returns a constant at runtime, which causes illegal production.
|
||||||
|
|
||||||
> Note that most compilers now have their own compiler optimizations.
|
> Note that most compilers now have their compiler optimizations.
|
||||||
> Many illegal behaviors become legal under the compiler's optimization.
|
> Many illegal behaviors become legal under the compiler's optimization.
|
||||||
> If you need to reproduce the error, you need to use the old version of the compiler.
|
> If you need to reproduce the error, you need to use the old version of the compiler.
|
||||||
|
|
||||||
C++11 provides `constexpr` to let the user explicitly declare that the function or
|
C++11 provides `constexpr` to let the user explicitly declare that the function or
|
||||||
object constructor will become a constant expression at compile time.
|
object constructor will become a constant expression at compile time.
|
||||||
This keyword explicitly tells the compiler that it should verify that `len_foo`
|
This keyword explicitly tells the compiler that it should verify that `len_foo`
|
||||||
should be a compile time. Constant expression.
|
should be a compile-time constant expression.
|
||||||
|
|
||||||
In addition, the function of `constexpr` can use recursion:
|
In addition, the function of `constexpr` can use recursion:
|
||||||
|
|
||||||
@@ -165,9 +164,9 @@ constexpr int fibonacci(const int n) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Starting with C++14,
|
Starting with C++14,
|
||||||
the constexpr function can use simple statements such as local variables,
|
the constexpr function can use simple statements such as local variables,
|
||||||
loops, and branches internally.
|
loops, and branches internally.
|
||||||
For example, the following code cannot be compiled under the C++11 standard:
|
For example, the following code cannot be compiled under the C++11 standard:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -178,7 +177,7 @@ constexpr int fibonacci(const int n) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To do this, we can write a simplified version like this
|
To do this, we can write a simplified version like this
|
||||||
to make the function available from C++11:
|
to make the function available from C++11:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -191,9 +190,9 @@ constexpr int fibonacci(const int n) {
|
|||||||
|
|
||||||
### if-switch
|
### if-switch
|
||||||
|
|
||||||
In traditional C++, the declaration of a variable can declare a temporary variable `int`
|
In traditional C++, the declaration of a variable can declare a temporary variable `int`
|
||||||
even though it can be located anywhere, even within a `for` statement,
|
even though it can be located anywhere, even within a `for` statement,
|
||||||
but there is always no way to declare a temporary variable in the `if` and `switch` statements.
|
but there is always no way to declare a temporary variable in the `if` and `switch` statements.
|
||||||
E.g:
|
E.g:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -204,7 +203,7 @@ E.g:
|
|||||||
int main() {
|
int main() {
|
||||||
std::vector<int> vec = {1, 2, 3, 4};
|
std::vector<int> vec = {1, 2, 3, 4};
|
||||||
|
|
||||||
// after c++17, can be simplefied by using `auto`
|
// since c++17, can be simplified by using `auto`
|
||||||
const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 2);
|
const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 2);
|
||||||
if (itr != vec.end()) {
|
if (itr != vec.end()) {
|
||||||
*itr = 3;
|
*itr = 3;
|
||||||
@@ -215,15 +214,16 @@ int main() {
|
|||||||
*itr = 4;
|
*itr = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// should output: 1, 4, 3, 4. can be simplefied using `auto`
|
// should output: 1, 4, 3, 4. can be simplified using `auto`
|
||||||
for (std::vector<int>::iterator element = vec.begin(); element != vec.end(); ++element)
|
for (std::vector<int>::iterator element = vec.begin(); element != vec.end();
|
||||||
|
++element)
|
||||||
std::cout << *element << std::endl;
|
std::cout << *element << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above code, we can see that the `itr` variable is defined in the scope of
|
In the above code, we can see that the `itr` variable is defined in the scope of
|
||||||
the entire `main()`, which causes us to rename the other when we need to traverse
|
the entire `main()`, which causes us to rename the other when a variable need to traverse
|
||||||
the entire `std::vectors` again. A variable. C++17 eliminates this limitation so that
|
the entire `std::vector` again. C++17 eliminates this limitation so that
|
||||||
we can do this in if(or switch):
|
we can do this in if(or switch):
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -237,17 +237,17 @@ Is it similar to the Go?
|
|||||||
|
|
||||||
### Initializer list
|
### Initializer list
|
||||||
|
|
||||||
Initialization is a very important language feature,
|
Initialization is a very important language feature,
|
||||||
the most common one is when the object is initialized.
|
the most common one is when the object is initialized.
|
||||||
In traditional C++, different objects have different initialization methods,
|
In traditional C++, different objects have different initialization methods,
|
||||||
such as ordinary arrays, PODs (**P**lain **O**ld **D**ata,
|
such as ordinary arrays, PODs (**P**lain **O**ld **D**ata,
|
||||||
ie classes without constructs, destructors, and virtual functions)
|
i.e. classes without constructs, destructors, and virtual functions)
|
||||||
Or struct type can be initialized with `{}`,
|
Or struct type can be initialized with `{}`,
|
||||||
which is what we call the initialization list.
|
which is what we call the initialization list.
|
||||||
For the initialization of the class object,
|
For the initialization of the class object,
|
||||||
you need to use the copy construct,
|
you need to use the copy construct,
|
||||||
or you need to use `()`.
|
or you need to use `()`.
|
||||||
These different methods are specific to each other and cannot be generic.
|
These different methods are specific to each other and cannot be generic.
|
||||||
E.g:
|
E.g:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -276,21 +276,24 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To solve this problem,
|
To solve this problem,
|
||||||
C++11 first binds the concept of the initialization list to the type
|
C++11 first binds the concept of the initialization list to the type
|
||||||
and calls it `std::initializer_list`,
|
and calls it `std::initializer_list`,
|
||||||
allowing the constructor or other function to use the initialization list
|
allowing the constructor or other function to use the initialization list
|
||||||
like a parameter, which is The initialization of class objects provides
|
like a parameter, which is the initialization of class objects provides
|
||||||
a unified bridge between normal arrays and POD initialization methods,
|
a unified bridge between normal arrays and POD initialization methods,
|
||||||
such as:
|
such as:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
class MagicFoo {
|
class MagicFoo {
|
||||||
public:
|
public:
|
||||||
std::vector<int> vec;
|
std::vector<int> vec;
|
||||||
MagicFoo(std::initializer_list<int> list) {
|
MagicFoo(std::initializer_list<int> list) {
|
||||||
for (std::initializer_list<int>::iterator it = list.begin();
|
for (std::initializer_list<int>::iterator it = list.begin();
|
||||||
it != list.end(); ++it)
|
it != list.end(); ++it)
|
||||||
vec.push_back(*it);
|
vec.push_back(*it);
|
||||||
}
|
}
|
||||||
@@ -300,20 +303,23 @@ int main() {
|
|||||||
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
||||||
|
|
||||||
std::cout << "magicFoo: ";
|
std::cout << "magicFoo: ";
|
||||||
for (std::vector<int>::iterator it = magicFoo.vec.begin(); it != magicFoo.vec.end(); ++it) std::cout << *it << std::endl;
|
for (std::vector<int>::iterator it = magicFoo.vec.begin();
|
||||||
|
it != magicFoo.vec.end(); ++it)
|
||||||
|
std::cout << *it << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This constructor is called the initialize list constructor, and the type with
|
This constructor is called the initialize list constructor, and the type with
|
||||||
this constructor will be specially taken care of during initialization.
|
this constructor will be specially taken care of during initialization.
|
||||||
|
|
||||||
In addition to the object construction, the initialization list can also
|
In addition to the object construction, the initialization list can also
|
||||||
be used as a formal parameter of a normal function, for example:
|
be used as a formal parameter of a normal function, for example:
|
||||||
|
|
||||||
```Cpp
|
```Cpp
|
||||||
public:
|
public:
|
||||||
void foo(std::initializer_list<int> list) {
|
void foo(std::initializer_list<int> list) {
|
||||||
for (std::initializer_list<int>::iterator it = list.begin(); it != list.end(); ++it) vec.push_back(*it);
|
for (std::initializer_list<int>::iterator it = list.begin();
|
||||||
|
it != list.end(); ++it) vec.push_back(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
magicFoo.foo({6,7,8,9});
|
magicFoo.foo({6,7,8,9});
|
||||||
@@ -327,21 +333,22 @@ Foo foo2 {3, 4};
|
|||||||
|
|
||||||
### Structured binding
|
### Structured binding
|
||||||
|
|
||||||
Structured bindings provide functionality similar to the multiple return values
|
Structured bindings provide functionality similar to the multiple return values
|
||||||
provided in other languages. In the chapter on containers,
|
provided in other languages. In the chapter on containers,
|
||||||
we will learn that C++11 has added a `std::tuple` container for
|
we will learn that C++11 has added a `std::tuple` container for
|
||||||
constructing a tuple that encloses multiple return values. But the flaw
|
constructing a tuple that encloses multiple return values. But the flaw
|
||||||
is that C++11/14 does not provide a simple way to get and define
|
is that C++11/14 does not provide a simple way to get and define
|
||||||
the elements in the tuple directly from the tuple,
|
the elements in the tuple from the tuple,
|
||||||
although we can unpack the tuple using `std::tie`
|
although we can unpack the tuple using `std::tie`
|
||||||
But we still have to be very clear about how many objects this tuple contains,
|
But we still have to be very clear about how many objects this tuple contains,
|
||||||
what type of each object is, very troublesome.
|
what type of each object is, very troublesome.
|
||||||
|
|
||||||
C++17 completes this setting,
|
C++17 completes this setting,
|
||||||
and the structured bindings let us write code like this:
|
and the structured bindings let us write code like this:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
std::tuple<int, double, std::string> f() {
|
std::tuple<int, double, std::string> f() {
|
||||||
return std::make_tuple(1, 2.3, "456");
|
return std::make_tuple(1, 2.3, "456");
|
||||||
@@ -354,12 +361,12 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `auto` type derivation is described in the
|
The `auto` type derivation is described in the
|
||||||
[auto type inference](#auto) section.
|
[auto type inference](#auto) section.
|
||||||
|
|
||||||
## 2.3 Type inference
|
## 2.3 Type inference
|
||||||
|
|
||||||
In traditional C and C++, the types of parameters must be clearly defined, which does not help us to quickly encode, especially when we are faced with a large number of complex template types, we must clearly indicate the type of variables in order to proceed. Subsequent coding, which not only slows down our development efficiency, but also makes the code stinking and long.
|
In traditional C and C++, the types of parameters must be clearly defined, which does not help us to quickly encode, especially when we are faced with a large number of complex template types, we must indicate the type of variables to proceed. Subsequent coding, which not only slows down our development efficiency but also makes the code stinking and long.
|
||||||
|
|
||||||
C++11 introduces the two keywords `auto` and `decltype` to implement type derivation, letting the compiler worry about the type of the variable. This makes C++ the same as other modern programming languages, in a way that provides the habit of not having to worry about variable types.
|
C++11 introduces the two keywords `auto` and `decltype` to implement type derivation, letting the compiler worry about the type of the variable. This makes C++ the same as other modern programming languages, in a way that provides the habit of not having to worry about variable types.
|
||||||
|
|
||||||
@@ -372,8 +379,8 @@ One of the most common and notable examples of type derivation using `auto` is t
|
|||||||
```cpp
|
```cpp
|
||||||
// before C++11
|
// before C++11
|
||||||
// cbegin() returns vector<int>::const_iterator
|
// cbegin() returns vector<int>::const_iterator
|
||||||
// and therefore itr is type vector<int>::const_iterator
|
// and therefore it is type vector<int>::const_iterator
|
||||||
for(vector<int>::const_iterator it = vec.cbegin(); itr != vec.cend(); ++it)
|
for(vector<int>::const_iterator it = vec.cbegin(); it != vec.cend(); ++it)
|
||||||
```
|
```
|
||||||
|
|
||||||
When we have `auto`:
|
When we have `auto`:
|
||||||
@@ -411,31 +418,38 @@ auto i = 5; // i as int
|
|||||||
auto arr = new auto(10); // arr as int *
|
auto arr = new auto(10); // arr as int *
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note**: `auto` cannot be used for function arguments, so the following
|
Since C++ 14, `auto` can even be used as function arguments in generic lambda expressions,
|
||||||
> is not possible to compile (considering overloading,
|
and such functionality is generalized to normal functions in C++ 20.
|
||||||
> we should use templates):
|
Consider the following example:
|
||||||
> ```cpp
|
|
||||||
> int add(auto x, auto y);
|
```cpp
|
||||||
|
auto add14 = [](auto x, auto y) -> int {
|
||||||
|
return x+y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add20(auto x, auto y) {
|
||||||
|
return x+y;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto i = 5; // type int
|
||||||
|
auto j = 6; // type int
|
||||||
|
std::cout << add14(i, j) << std::endl;
|
||||||
|
std::cout << add20(i, j) << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: `auto` cannot be used to derive array types yet:
|
||||||
>
|
>
|
||||||
> 2.6.auto.cpp:16:9: error: 'auto' not allowed in function prototype
|
|
||||||
> int add(auto x, auto y) {
|
|
||||||
> ^~~~
|
|
||||||
> ```
|
|
||||||
>
|
|
||||||
> In addition, `auto` cannot be used to derive array types:
|
|
||||||
>
|
|
||||||
> ```cpp
|
> ```cpp
|
||||||
> auto auto_arr2[10] = arr; // illegal, can't infer array type
|
> auto auto_arr2[10] = {arr}; // illegal, can't infer array type
|
||||||
>
|
>
|
||||||
> 2.6.auto.cpp:30:19: error: 'auto_arr2' declared as array of 'auto'
|
> 2.6.auto.cpp:30:19: error: 'auto_arr2' declared as array of 'auto'
|
||||||
> auto auto_arr2[10] = arr;
|
> auto auto_arr2[10] = {arr};
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
### decltype
|
### decltype
|
||||||
|
|
||||||
The `decltype` keyword is used to solve the defect that the auto keyword
|
The `decltype` keyword is used to solve the defect that the auto keyword
|
||||||
can only type the variable. Its usage is very similar to `sizeof`:
|
can only type the variable. Its usage is very similar to `typeof`:
|
||||||
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
decltype(expression)
|
decltype(expression)
|
||||||
@@ -449,9 +463,9 @@ auto y = 2;
|
|||||||
decltype(x+y) z;
|
decltype(x+y) z;
|
||||||
```
|
```
|
||||||
|
|
||||||
You have seen in the previous example that
|
You have seen in the previous example that
|
||||||
`decltype` is used to infer the usage of the type.
|
`decltype` is used to infer the usage of the type.
|
||||||
The following example is to determine
|
The following example is to determine
|
||||||
if the above variables `x, y, z` are of the same type:
|
if the above variables `x, y, z` are of the same type:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -463,7 +477,7 @@ if (std::is_same<decltype(x), decltype(z)>::value)
|
|||||||
std::cout << "type z == type x" << std::endl;
|
std::cout << "type z == type x" << std::endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
Among them, `std::is_same<T, U>` is used to determine whether
|
Among them, `std::is_same<T, U>` is used to determine whether
|
||||||
the two types `T` and `U` are equal. The output is:
|
the two types `T` and `U` are equal. The output is:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -473,34 +487,34 @@ type z == type x
|
|||||||
|
|
||||||
### tail type inference
|
### tail type inference
|
||||||
|
|
||||||
You may think that when we introduce `auto`, we have already mentioned that `auto` cannot be used for function arguments for type derivation. Can `auto` be used to derive the return type of a function? Still consider an example of an add function, which we have to write in traditional C++:
|
You may think that whether `auto` can be used to deduce the return type of a function. Still consider an example of an add function, which we have to write in traditional C++:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<typename R, typename T, typename U>
|
template<typename R, typename T, typename U>
|
||||||
R add(T x, U y) {
|
R add(T x, U y) {
|
||||||
return x+y
|
return x+y;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note: There is no difference between typename and class in the template parameter list. Before the keyword typename appears, class is used to define the template parameters. However, when defining a variable with [nested dependency type](http://en.cppreference.com/w/cpp/language/dependent_name#The_typename_disambiguator_for_dependent_names) in the template, you need to use typename to eliminate ambiguity.
|
> Note: There is no difference between typename and class in the template parameter list. Before the keyword typename appears, class is used to define the template parameters. However, when defining a variable with [nested dependency type](https://en.cppreference.com/w/cpp/language/dependent_name#The_typename_disambiguator_for_dependent_names) in the template, you need to use typename to eliminate ambiguity.
|
||||||
|
|
||||||
Such code is actually very ugly, because the programmer must explicitly
|
Such code is very ugly because the programmer must explicitly
|
||||||
indicate the return type when using this template function.
|
indicate the return type when using this template function.
|
||||||
But in fact we don't know what kind of operation
|
But in fact, we don't know what kind of operation
|
||||||
the `add()` function will do, and what kind of return type to get.
|
the `add()` function will do, and what kind of return type to get.
|
||||||
|
|
||||||
This problem was solved in C++11. Although you may immediately
|
This problem was solved in C++11. Although you may immediately
|
||||||
react to using `decltype` to derive the type of `x+y`,
|
react to using `decltype` to derive the type of `x+y`,
|
||||||
write something like this:
|
write something like this:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
decltype(x+y) add(T x, U y)
|
decltype(x+y) add(T x, U y)
|
||||||
```
|
```
|
||||||
|
|
||||||
But in fact, this way of writing can not be compiled.
|
But in fact, this way of writing can not be compiled.
|
||||||
This is because `x` and `y` have not been defined
|
This is because `x` and `y` have not been defined
|
||||||
when the compiler reads decltype(x+y).
|
when the compiler reads decltype(x+y).
|
||||||
To solve this problem, C++11 also introduces a trailing return type,
|
To solve this problem, C++11 also introduces a trailing return type,
|
||||||
which uses the auto keyword to post the return type:
|
which uses the auto keyword to post the return type:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -510,7 +524,7 @@ auto add2(T x, U y) -> decltype(x+y){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The good news is that from C++14 it is possible to directly derive the return value of
|
The good news is that from C++14 it is possible to directly derive the return value of
|
||||||
a normal function, so the following way becomes legal:
|
a normal function, so the following way becomes legal:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -539,16 +553,16 @@ std::cout << "q: " << q << std::endl;
|
|||||||
|
|
||||||
`decltype(auto)` is a slightly more complicated use of C++14.
|
`decltype(auto)` is a slightly more complicated use of C++14.
|
||||||
|
|
||||||
> To understand it you need to know the concept of parameter forwarding
|
> To understand it you need to know the concept of parameter forwarding
|
||||||
> in C++, which we will cover in detail in the
|
> in C++, which we will cover in detail in the
|
||||||
> [Language Runtime Hardening](./03-runtime.md) chapter,
|
> [Language Runtime Enhancements](./03-runtime.md) chapter,
|
||||||
> and you can come back to the contents of this section later.
|
> and you can come back to the contents of this section later.
|
||||||
|
|
||||||
In simple terms, `decltype(auto)` is mainly used to derive
|
In simple terms, `decltype(auto)` is mainly used to derive
|
||||||
the return type of a forwarding function or package,
|
the return type of a forwarding function or package,
|
||||||
which does not require us to explicitly specify
|
which does not require us to explicitly specify
|
||||||
the parameter expression of `decltype`.
|
the parameter expression of `decltype`.
|
||||||
Consider the following example, when we need to wrap the following
|
Consider the following example, when we need to wrap the following
|
||||||
two functions:
|
two functions:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -582,7 +596,7 @@ decltype(auto) look_up_a_string_2() {
|
|||||||
|
|
||||||
### if constexpr
|
### if constexpr
|
||||||
|
|
||||||
As we saw at the beginning of this chapter, we know that C++11 introduces the `constexpr` keyword, which compiles expressions or functions into constant results. A natural idea is that if we introduce this feature into the conditional judgment, let the code complete the branch judgment at compile time, can it make the program more efficient? C++17 introduces the `constexpr` keyword into the `if` statement, allowing you to declare the condition of a constant expression in your code. Consider the following code:
|
As we saw at the beginning of this chapter, we know that C++11 introduces the `constexpr` keyword, which compiles expressions or functions into constant results. A natural idea is that if we introduce this feature into the conditional judgment, let the code complete the branch judgment at compile-time, can it make the program more efficient? C++17 introduces the `constexpr` keyword into the `if` statement, allowing you to declare the condition of a constant expression in your code. Consider the following code:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -618,7 +632,7 @@ int main() {
|
|||||||
|
|
||||||
### Range-based for loop
|
### Range-based for loop
|
||||||
|
|
||||||
Finally, C++11 introduces a range-based iterative method, and we have the ability to write loops that are as concise
|
Finally, C++11 introduces a range-based iterative method, and we can write loops that are as concise
|
||||||
as Python, and we can further simplify the previous example:
|
as Python, and we can further simplify the previous example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -641,7 +655,7 @@ int main() {
|
|||||||
|
|
||||||
## 2.5 Templates
|
## 2.5 Templates
|
||||||
|
|
||||||
C++ templates have always been a special art of the language, and templates can even be used independently as a new language. The philosophy of the template is to throw all the problems that can be processed at compile time into the compile time, and only deal with those core dynamic services at runtime, so as to greatly optimize the performance of the runtime. Therefore, templates are also regarded by many as one of the black magic of C++.
|
C++ templates have always been a special art of the language, and templates can even be used independently as a new language. The philosophy of the template is to throw all the problems that can be processed at compile time into the compile time, and only deal with those core dynamic services at runtime, to greatly optimize the performance of the runtime. Therefore, templates are also regarded by many as one of the black magic of C++.
|
||||||
|
|
||||||
### Extern templates
|
### Extern templates
|
||||||
|
|
||||||
@@ -662,9 +676,9 @@ In the traditional C++ compiler, `>>` is always treated as a right shift operato
|
|||||||
std::vector<std::vector<int>> matrix;
|
std::vector<std::vector<int>> matrix;
|
||||||
```
|
```
|
||||||
|
|
||||||
This is not compiled under the traditional C++ compiler,
|
This is not compiled under the traditional C++ compiler,
|
||||||
and C++11 starts with continuous right angle brackets that become legal
|
and C++11 starts with continuous right angle brackets that become legal
|
||||||
and can be compiled successfully.
|
and can be compiled successfully.
|
||||||
Even the following writing can be compiled by:
|
Even the following writing can be compiled by:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -696,7 +710,7 @@ typedef MagicType<std::vector<T>, std::string> FakeDarkMagic;
|
|||||||
|
|
||||||
C++11 uses `using` to introduce the following form of writing, and at the same time supports the same effect as the traditional `typedef`:
|
C++11 uses `using` to introduce the following form of writing, and at the same time supports the same effect as the traditional `typedef`:
|
||||||
|
|
||||||
> Usually we use `typedef` to define the alias syntax: `typedef original name new name; `, but the definition syntax for aliases such as function pointers is different, which usually causes a certain degree of difficulty for direct reading.
|
> Usually, we use `typedef` to define the alias syntax: `typedef original name new name; `, but the definition syntax for aliases such as function pointers is different, which usually causes a certain degree of difficulty for direct reading.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
typedef int (*process)(void *);
|
typedef int (*process)(void *);
|
||||||
@@ -709,43 +723,21 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Default template parameters
|
|
||||||
|
|
||||||
We may have defined an addition function:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
template<typename T, typename U>
|
|
||||||
auto add(T x, U y) -> decltype(x+y) {
|
|
||||||
return x+y;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
However, when used, it is found that to use add, you must specify the type of its template parameters each time.
|
|
||||||
|
|
||||||
A convenience is provided in C++11 to specify the default parameters of the template:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
template<typename T = int, typename U = int>
|
|
||||||
auto add(T x, U y) -> decltype(x+y) {
|
|
||||||
return x+y;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Variadic templates
|
### Variadic templates
|
||||||
|
|
||||||
The template has always been one of C++'s unique **Black Magic**.
|
The template has always been one of C++'s unique **Black Magic**.
|
||||||
In traditional C++,
|
In traditional C++,
|
||||||
both a class template and a function template could only accept
|
both a class template and a function template could only accept
|
||||||
a fixed set of template parameters as specified;
|
a fixed set of template parameters as specified;
|
||||||
C++11 added a new representation, allowing any number,
|
C++11 added a new representation, allowing any number,
|
||||||
template parameters of any category,
|
template parameters of any category,
|
||||||
and there is no need to fix the number of parameters when defining.
|
and there is no need to fix the number of parameters when defining.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<typename... Ts> class Magic;
|
template<typename... Ts> class Magic;
|
||||||
```
|
```
|
||||||
|
|
||||||
The template class Magic object can accept unrestricted number of typename as
|
The template class Magic object can accept an unrestricted number of typename as
|
||||||
a formal parameter of the template, such as the following definition:
|
a formal parameter of the template, such as the following definition:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -763,19 +755,20 @@ If you do not want to generate 0 template parameters, you can manually define at
|
|||||||
template<typename Require, typename... Args> class Magic;
|
template<typename Require, typename... Args> class Magic;
|
||||||
```
|
```
|
||||||
|
|
||||||
The variable length parameter template can also be directly adjusted to the template function.
|
The variable length parameter template can also be directly adjusted to the template function.
|
||||||
The `printf` function in the traditional C, although it can also reach the call of an indefinite number of formal parameters, is not class safe. In addition to the variable-length parameter functions that define class safety, C++11 can also make printf-like functions naturally handle objects that are not self-contained. In addition to the use of `...` in the template parameters to indicate the indefinite length of the template parameters, the function parameters also use the same representation to represent the indefinite length parameters, which provides a convenient means for us to simply write variable length parameter functions, such as:
|
The `printf` function in the traditional C, although it can also reach the call of an indefinite number of formal parameters, is not class safe. In addition to the variable-length parameter functions that define class safety, C++11 can also make printf-like functions naturally handle objects that are not self-contained. In addition to the use of `...` in the template parameters to indicate the indefinite length of the template parameters, the function parameters also use the same representation to represent the indefinite length parameters, which provides a convenient means for us to simply write variable length parameter functions, such as:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<typename... Args> void printf(const std::string &str, Args... args);
|
template<typename... Args> void printf(const std::string &str, Args... args);
|
||||||
```
|
```
|
||||||
|
|
||||||
Then we define variable length template parameters,
|
Then we define variable length template parameters,
|
||||||
how to unpack the parameters?
|
how to unpack the parameters?
|
||||||
|
|
||||||
First, we can use `sizeof...` to calculate the number of arguments:
|
First, we can use `sizeof...` to calculate the number of arguments:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
template<typename... Ts>
|
template<typename... Ts>
|
||||||
void magic(Ts... args) {
|
void magic(Ts... args) {
|
||||||
std::cout << sizeof...(args) << std::endl;
|
std::cout << sizeof...(args) << std::endl;
|
||||||
@@ -790,14 +783,13 @@ magic(1); // 1
|
|||||||
magic(1, ""); // 2
|
magic(1, ""); // 2
|
||||||
```
|
```
|
||||||
|
|
||||||
Second, the parameters are unpacked. So far there is no simple way to process
|
Second, the parameters are unpacked. So far there is no simple way to process
|
||||||
the parameter package, but there are two classic processing methods:
|
the parameter package, but there are two classic processing methods:
|
||||||
|
|
||||||
**1. Recursive template function**
|
**1. Recursive template function**
|
||||||
|
|
||||||
Recursion is a very easy way to think of and the most classic approach. This method continually recursively passes template parameters to the function, thereby achieving the purpose of recursively traversing all template parameters:
|
Recursion is a very easy way to think of and the most classic approach. This method continually recursively passes template parameters to the function, thereby achieving the purpose of recursively traversing all template parameters:
|
||||||
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
template<typename T0>
|
template<typename T0>
|
||||||
@@ -831,7 +823,7 @@ void printf2(T0 t0, T... t) {
|
|||||||
|
|
||||||
**3. Initialize list expansion**
|
**3. Initialize list expansion**
|
||||||
|
|
||||||
Recursive template functions are a standard practice, but the obvious drawback is that you must define a function that terminates recursion.
|
Recursive template functions are standard practice, but the obvious drawback is that you must define a function that terminates recursion.
|
||||||
|
|
||||||
Here is a description of the black magic that is expanded using the initialization list:
|
Here is a description of the black magic that is expanded using the initialization list:
|
||||||
|
|
||||||
@@ -871,14 +863,14 @@ What we mainly mentioned above is a form of template parameters: type template p
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
auto add(T t, U u) {
|
auto add(T t, U u) {
|
||||||
return t+u;
|
return t+u;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The parameters of the template `T` and `U` are specific types.
|
The parameters of the template `T` and `U` are specific types.
|
||||||
But there is also a common form of template parameter that allows different literals
|
But there is also a common form of template parameter that allows different literals
|
||||||
to be template parameters, ie non-type template parameters:
|
to be template parameters, i.e. non-type template parameters:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template <typename T, int BufSize>
|
template <typename T, int BufSize>
|
||||||
@@ -894,7 +886,7 @@ buffer_t<int, 100> buf; // 100 as template parameter
|
|||||||
```
|
```
|
||||||
|
|
||||||
In this form of template parameters, we can pass `100` as a parameter to the template.
|
In this form of template parameters, we can pass `100` as a parameter to the template.
|
||||||
After C++11 introduced the feature of type derivation, we will naturally ask, since the template parameters here
|
After C++11 introduced the feature of type derivation, we will naturally ask, since the template parameters here.
|
||||||
Passing with a specific literal, can the compiler assist us in type derivation,
|
Passing with a specific literal, can the compiler assist us in type derivation,
|
||||||
By using the placeholder `auto`, there is no longer a need to explicitly specify the type?
|
By using the placeholder `auto`, there is no longer a need to explicitly specify the type?
|
||||||
Fortunately, C++17 introduces this feature, and we can indeed use the `auto` keyword to let the compiler assist in the completion of specific types of derivation.
|
Fortunately, C++17 introduces this feature, and we can indeed use the `auto` keyword to let the compiler assist in the completion of specific types of derivation.
|
||||||
@@ -915,10 +907,11 @@ int main() {
|
|||||||
|
|
||||||
### Delegate constructor
|
### Delegate constructor
|
||||||
|
|
||||||
C++11 introduces the concept of a delegate construct, which allows a constructor to call another constructor
|
C++11 introduces the concept of a delegate construct, which allows a constructor to call another constructor
|
||||||
in a constructor in the same class, thus simplifying the code:
|
in a constructor in the same class, thus simplifying the code:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
class Base {
|
class Base {
|
||||||
public:
|
public:
|
||||||
int value1;
|
int value1;
|
||||||
@@ -943,6 +936,7 @@ int main() {
|
|||||||
In traditional C++, constructors need to pass arguments one by one if they need inheritance, which leads to inefficiency. C++11 introduces the concept of inheritance constructors using the keyword using:
|
In traditional C++, constructors need to pass arguments one by one if they need inheritance, which leads to inefficiency. C++11 introduces the concept of inheritance constructors using the keyword using:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
class Base {
|
class Base {
|
||||||
public:
|
public:
|
||||||
int value1;
|
int value1;
|
||||||
@@ -956,7 +950,7 @@ public:
|
|||||||
};
|
};
|
||||||
class Subclass : public Base {
|
class Subclass : public Base {
|
||||||
public:
|
public:
|
||||||
using Base::Base; // inhereit constructor
|
using Base::Base; // inheritance constructor
|
||||||
};
|
};
|
||||||
int main() {
|
int main() {
|
||||||
Subclass s(3);
|
Subclass s(3);
|
||||||
@@ -984,7 +978,7 @@ C++11 introduces the two keywords `override` and `final` to prevent this from ha
|
|||||||
|
|
||||||
### override
|
### override
|
||||||
|
|
||||||
When overriding a virtual function, introducing the `override` keyword will explicitly tell the compiler to overload, and the compiler will check if the base function has such a virtual function, otherwise it will not compile:
|
When overriding a virtual function, introducing the `override` keyword will explicitly tell the compiler to overload, and the compiler will check if the base function has such a virtual function with consistent function signature, otherwise it will not compile:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct Base {
|
struct Base {
|
||||||
@@ -998,7 +992,7 @@ struct SubClass: Base {
|
|||||||
|
|
||||||
### final
|
### final
|
||||||
|
|
||||||
`final` is to prevent the class from being continued to inherit and to terminate
|
`final` is to prevent the class from being continued to inherit and to terminate
|
||||||
the virtual function to continue to be overloaded.
|
the virtual function to continue to be overloaded.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -1018,7 +1012,7 @@ struct SubClass3: Base {
|
|||||||
|
|
||||||
### Explicit delete default function
|
### Explicit delete default function
|
||||||
|
|
||||||
In traditional C++, if the programmer does not provide it, the compiler will default to generating default constructors, copy constructs, assignment operators, and destructors for the object. In addition, C++ also defines operators such as `new` `delete` for all classes. This part of the function can be overridden when the programmer needs it.
|
In traditional C++, if the programmer does not provide it, the compiler will default to generating default constructors, copy constructs, assignment operators, and destructors for the object. Besides, C++ also defines operators such as `new` `delete` for all classes. This part of the function can be overridden when the programmer needs it.
|
||||||
|
|
||||||
This raises some requirements: the ability to accurately control the generation of default functions cannot be controlled. For example, when copying a class is prohibited, the copy constructor and the assignment operator must be declared as `private`. Trying to use these undefined functions will result in compilation or link errors, which is a very unconventional way.
|
This raises some requirements: the ability to accurately control the generation of default functions cannot be controlled. For example, when copying a class is prohibited, the copy constructor and the assignment operator must be declared as `private`. Trying to use these undefined functions will result in compilation or link errors, which is a very unconventional way.
|
||||||
|
|
||||||
@@ -1037,7 +1031,7 @@ class Magic {
|
|||||||
|
|
||||||
### Strongly typed enumerations
|
### Strongly typed enumerations
|
||||||
|
|
||||||
In traditional C++, enumerated types are not type-safe, and enumerated types are treated as integers, which allows two completely different enumerated types to be directly compared (although the compiler gives the check, but not all) , ** Even the enumeration value names of different enum types in the same namespace cannot be the same**, which is usually not what we want to see.
|
In traditional C++, enumerated types are not type-safe, and enumerated types are treated as integers, which allows two completely different enumerated types to be directly compared (although the compiler gives the check, but not all), ** Even the enumeration value names of different enum types in the same namespace cannot be the same**, which is usually not what we want to see.
|
||||||
|
|
||||||
C++11 introduces an enumeration class and declares it using the syntax of `enum class`:
|
C++11 introduces an enumeration class and declares it using the syntax of `enum class`:
|
||||||
|
|
||||||
@@ -1065,7 +1059,9 @@ And we want to get the value of the enumeration value, we will have to explicitl
|
|||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -1081,7 +1077,7 @@ std::cout << new_enum::value3 << std::endl
|
|||||||
|
|
||||||
This section introduces the enhancements to language usability in modern C++, which I believe are the most important features that almost everyone needs to know and use:
|
This section introduces the enhancements to language usability in modern C++, which I believe are the most important features that almost everyone needs to know and use:
|
||||||
|
|
||||||
1. auto type derivation
|
1. Auto type derivation
|
||||||
2. Scope for iteration
|
2. Scope for iteration
|
||||||
3. Initialization list
|
3. Initialization list
|
||||||
4. Variable parameter template
|
4. Variable parameter template
|
||||||
@@ -1091,6 +1087,10 @@ This section introduces the enhancements to language usability in modern C++, wh
|
|||||||
1. Using structured binding, implement the following functions with just one line of function code:
|
1. Using structured binding, implement the following functions with just one line of function code:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
template <typename Key, typename Value, typename F>
|
template <typename Key, typename Value, typename F>
|
||||||
void update(std::map<Key, Value>& m, F foo) {
|
void update(std::map<Key, Value>& m, F foo) {
|
||||||
// TODO:
|
// TODO:
|
||||||
@@ -1117,4 +1117,4 @@ This section introduces the enhancements to language usability in modern C++, wh
|
|||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ order: 3
|
|||||||
|
|
||||||
## 3.1 Lambda Expression
|
## 3.1 Lambda Expression
|
||||||
|
|
||||||
Lambda expressions are one of the most important features in modern C++, and Lambda expressions actually provide a feature like anonymous functions.
|
Lambda expressions are one of the most important features in modern C++, and Lambda expressions provide a feature like anonymous functions.
|
||||||
Anonymous functions are used when a function is needed, but you don't want to use a function to name a function. There are actually many, many scenes like this.
|
Anonymous functions are used when a function is needed, but you don’t want to use a name to call a function. There are many, many scenes like this.
|
||||||
So anonymous functions are almost standard on modern programming languages.
|
So anonymous functions are almost standard in modern programming languages.
|
||||||
|
|
||||||
### Basics
|
### Basics
|
||||||
|
|
||||||
@@ -24,22 +24,22 @@ The basic syntax of a Lambda expression is as follows:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The above grammar rules are well understood except for the things in `[catch list]`,
|
The above grammar rules are well understood except for the things in `[capture list]`,
|
||||||
except that the function name of the general function is omitted.
|
except that the function name of the general function is omitted.
|
||||||
The return value is in the form of a `->`
|
The return value is in the form of a `->`
|
||||||
(we have already mentioned this in the tail return type earlier in the previous section).
|
(we have already mentioned this in the tail return type earlier in the previous section).
|
||||||
|
|
||||||
The so-called capture list can be understood as a type of parameter.
|
The so-called capture list can be understood as a type of parameter.
|
||||||
The internal function body of a lambda expression cannot use variables outside
|
The internal function body of a lambda expression cannot use variables outside
|
||||||
the body of the function by default.
|
the body of the function by default.
|
||||||
At this time, the capture list can serve to transfer external data.
|
At this time, the capture list can serve to transfer external data.
|
||||||
According to the behavior passed,
|
According to the behavior passed,
|
||||||
the capture list is also divided into the following types:
|
the capture list is also divided into the following types:
|
||||||
|
|
||||||
#### 1. Value capture
|
#### 1. Value capture
|
||||||
|
|
||||||
Similar to parameter passing, the value capture is based on the fact that
|
Similar to parameter passing, the value capture is based on the fact that
|
||||||
the variable can be copied, except that the captured variable is copied
|
the variable can be copied, except that the captured variable is copied
|
||||||
when the lambda expression is created, not when it is called:
|
when the lambda expression is created, not when it is called:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -76,35 +76,39 @@ void lambda_reference_capture() {
|
|||||||
|
|
||||||
#### 3. Implicit capture
|
#### 3. Implicit capture
|
||||||
|
|
||||||
Manually writing a capture list is sometimes very complicated.
|
Manually writing a capture list is sometimes very complicated.
|
||||||
This mechanical work can be handled by the compiler.
|
This mechanical work can be handled by the compiler.
|
||||||
At this point, you can write a `&` or `=` to the compiler to
|
At this point, you can write a `&` or `=` to the compiler to
|
||||||
declare the reference or value capture.
|
declare the reference or value capture.
|
||||||
|
|
||||||
To summarize, capture provides the ability for lambda expressions
|
To summarize, capture provides the ability for lambda expressions
|
||||||
to use external values. The four most common forms of
|
to use external values. The four most common forms of
|
||||||
capture lists can be:
|
capture lists can be:
|
||||||
|
|
||||||
- \[\] empty capture list
|
- \[\] empty capture list
|
||||||
- \[name1, name2, ...\] captures a series of variables
|
- \[name1, name2, ...\] captures a series of variables
|
||||||
- \[&\] reference capture, let the compiler derive the capture list by itself
|
- \[&\] reference capture, determine the reference capture list from the uses the in function body
|
||||||
- \[=\] value capture, let the compiler execute the list of derivation applications
|
- \[=\] value capture, determine the value capture list from the uses in the function body
|
||||||
|
|
||||||
#### 4. Expression capture
|
#### 4. Expression capture
|
||||||
|
|
||||||
> This section needs to understand the rvalue references and smart pointers that
|
> This section needs to understand the rvalue references and smart pointers that
|
||||||
> will be mentioned later.
|
> will be mentioned later.
|
||||||
|
|
||||||
The value captures and reference captures mentioned above are variables that have been
|
The value captures and reference captures mentioned above are variables that have been
|
||||||
declared in the outer scope, so these capture methods capture the lvalue
|
declared in the outer scope, so these capture methods capture the lvalue
|
||||||
and not capture the rvalue.
|
and not capture the rvalue.
|
||||||
|
|
||||||
C++14 gives us the convenience of allowing the captured members to be initialized
|
C++14 gives us the convenience of allowing the captured members to be initialized
|
||||||
with arbitrary expressions, which allows the capture of rvalues.
|
with arbitrary expressions, which allows the capture of rvalues.
|
||||||
The type of the captured variable being declared is judged according to the expression,
|
The type of the captured variable being declared is judged according to the expression,
|
||||||
and the judgment is the same as using `auto`:
|
and the judgment is the same as using `auto`:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory> // std::make_unique
|
||||||
|
#include <utility> // std::move
|
||||||
|
|
||||||
void lambda_expression_capture() {
|
void lambda_expression_capture() {
|
||||||
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 {
|
||||||
@@ -114,28 +118,24 @@ void lambda_expression_capture() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above code, `important` is an exclusive pointer that cannot be caught.
|
In the above code, `important` is an exclusive pointer that cannot be caught by value capture using `=`.
|
||||||
At this time we need to transfer it to the rvalue and
|
At this time we need to transfer it to the rvalue and
|
||||||
initialize it in the expression.
|
initialize it in the expression.
|
||||||
|
|
||||||
### Generic Lambda
|
### Generic Lambda
|
||||||
|
|
||||||
In the previous section we mentioned that the `auto` keyword cannot be used
|
In the previous section, we mentioned that the `auto` keyword cannot be used
|
||||||
in the parameter list because it would conflict with the functionality of the template.
|
in the parameter list because it would conflict with the functionality of the template.
|
||||||
But Lambda expressions are not ordinary functions, so Lambda expressions are not templated.
|
But lambda expressions are not regular functions, without further specification on the typed parameter list, lambda expressions cannot utilize templates. Fortunately, this trouble
|
||||||
This has caused us some trouble: the parameter table cannot be generalized,
|
only exists in C++11, starting with C++14. The formal parameters of the lambda function
|
||||||
and the parameter table type must be clarified.
|
can use the `auto` keyword to utilize template generics:
|
||||||
|
|
||||||
Fortunately, this trouble only exists in C++11, starting with C++14.
|
|
||||||
The formal parameters of the Lambda function can use the `auto` keyword
|
|
||||||
to generate generic meanings:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void lambda_generic() {
|
void lambda_generic() {
|
||||||
auto generic = [](auto x, auto y) {
|
auto generic = [](auto x, auto y) {
|
||||||
return x+y;
|
return x+y;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::cout << generic(1, 2) << std::endl;
|
std::cout << generic(1, 2) << std::endl;
|
||||||
std::cout << generic(1.1, 2.2) << std::endl;
|
std::cout << generic(1.1, 2.2) << std::endl;
|
||||||
}
|
}
|
||||||
@@ -143,15 +143,15 @@ void lambda_generic() {
|
|||||||
|
|
||||||
## 3.2 Function Object Wrapper
|
## 3.2 Function Object Wrapper
|
||||||
|
|
||||||
Although this part of the standard library is part of the standard library,
|
Although the features are part of the standard library and not found in runtime,
|
||||||
it enhances the runtime capabilities of the C++ language.
|
it enhances the runtime capabilities of the C++ language.
|
||||||
This part of the content is also very important, so put it here for introduction.
|
This part of the content is also very important, so put it here for the introduction.
|
||||||
|
|
||||||
### `std::function`
|
### `std::function`
|
||||||
|
|
||||||
The essence of a Lambda expression is an object of a class type (called a closure type)
|
The essence of a Lambda expression is an object of a class type (called a closure type)
|
||||||
that is similar to a function object type (called a closure object).
|
that is similar to a function object type (called a closure object).
|
||||||
When the capture list of a Lambda expression is empty, the closure object
|
When the capture list of a Lambda expression is empty, the closure object
|
||||||
can also be converted to a function pointer value for delivery, for example:
|
can also be converted to a function pointer value for delivery, for example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -170,20 +170,19 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The above code gives two different forms of invocation, one is to call Lambda
|
The above code gives two different forms of invocation, one is to call Lambda
|
||||||
as a function type, and the other is to directly call a Lambda expression.
|
as a function type, and the other is to directly call a Lambda expression.
|
||||||
In C++11, these concepts are unified.
|
In C++11, these concepts are unified.
|
||||||
The type of object that can be called is collectively called the callable type.
|
The type of object that can be called is collectively called the callable type.
|
||||||
This type is introduced by `std::function`.
|
This type is introduced by `std::function`.
|
||||||
|
|
||||||
C++11 `std::function` is a generic, polymorphic function wrapper
|
C++11 `std::function` is a generic, polymorphic function wrapper
|
||||||
whose instances can store, copy, and call any target entity that can be called.
|
whose instances can store, copy, and call any target entity that can be called.
|
||||||
It is also an existing callable to C++. A type-safe package of entities (relatively,
|
It is also an existing callable to C++. A type-safe package of entities (relatively,
|
||||||
the call to a function pointer is not type-safe), in other words,
|
the call to a function pointer is not type-safe), in other words,
|
||||||
a container of functions. When we have a container for functions,
|
a container of functions. When we have a container for functions,
|
||||||
we can more easily handle functions and function pointers as objects. e.g:
|
we can more easily handle functions and function pointers as objects. e.g:
|
||||||
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -207,10 +206,10 @@ int main() {
|
|||||||
|
|
||||||
### `std::bind` and `std::placeholder`
|
### `std::bind` and `std::placeholder`
|
||||||
|
|
||||||
And `std::bind` is used to bind the parameters of the function call.
|
And `std::bind` is used to bind the parameters of the function call.
|
||||||
It solves the requirement that we may not always be able to get all the parameters
|
It solves the requirement that we may not always be able to get all the parameters
|
||||||
of a function at one time. Through this function, we can Part of the call parameters
|
of a function at one time. Through this function, we can Part of the call parameters
|
||||||
are bound to the function in advance to become a new object,
|
are bound to the function in advance to become a new object,
|
||||||
and then complete the call after the parameters are complete. e.g:
|
and then complete the call after the parameters are complete. e.g:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -218,53 +217,81 @@ int foo(int a, int b, int c) {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
// bind parameter 1, 2 on function foo, and use std::placeholders::_1 as placeholder
|
// bind parameter 1, 2 on function foo,
|
||||||
// for the first parameter.
|
// and use std::placeholders::_1 as placeholder for the first parameter.
|
||||||
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
|
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
|
||||||
// when call bindFoo, we only need one param left
|
// when call bindFoo, we only need one param left
|
||||||
bindFoo(1);
|
bindFoo(1);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Tip:** Note the magic of the `auto` keyword. Sometimes we may not be familiar
|
> **Tip:** Note the magic of the `auto` keyword. Sometimes we may not be familiar
|
||||||
> with the return type of a function, but we can circumvent this problem by using `auto`.
|
> with the return type of a function, but we can circumvent this problem by using `auto`.
|
||||||
|
|
||||||
## 3.3 rvalue Reference
|
## 3.3 rvalue Reference
|
||||||
|
|
||||||
rvalue references are one of the important features introduced by C++11
|
rvalue references are one of the important features introduced by C++11
|
||||||
that are synonymous with Lambda expressions. Its introduction solves
|
that are synonymous with Lambda expressions. Its introduction solves
|
||||||
a large number of historical issues in C++.
|
a large number of historical issues in C++.
|
||||||
Eliminating extra overhead such as `std::vector`, `std::string`,
|
Eliminating extra overhead such as `std::vector`, `std::string`,
|
||||||
and making the function object container `std::function` possible.
|
and making the function object container `std::function` possible.
|
||||||
|
|
||||||
### lvalue, rvalue, prvalue, xvalue
|
### lvalue, rvalue, prvalue, xvalue
|
||||||
|
|
||||||
To understand what the rvalue reference is all about, you must have a clear
|
To understand what the rvalue reference is all about, you must have a clear
|
||||||
understanding of the lvalue and the rvalue.
|
understanding of the lvalue and the rvalue.
|
||||||
|
|
||||||
**lvalue, left value**, as the name implies, is the value to the left of the assignment
|
**lvalue, left value**, as the name implies, is the value to the left of the assignment
|
||||||
symbol. To be precise, an lvalue is a persistent object that still exists after
|
symbol. To be precise, an lvalue is a persistent object that still exists after
|
||||||
an expression (not necessarily an assignment expression).
|
an expression (not necessarily an assignment expression).
|
||||||
|
|
||||||
**Rvalue, right value**, the value on the right refers to the temporary object
|
**Rvalue, right value**, the value on the right refers to the temporary object
|
||||||
that no longer exists after the expression ends.
|
that no longer exists after the expression ends.
|
||||||
|
|
||||||
In C++11, in order to introduce powerful rvalue references,
|
In C++11, in order to introduce powerful rvalue references,
|
||||||
the concept of rvalue values is further divided into:
|
the concept of rvalue values is further divided into:
|
||||||
prvalue, and xvalue.
|
prvalue, and xvalue.
|
||||||
|
|
||||||
**pvalue, pure rvalue**, purely rvalue, either purely literal,
|
**pvalue, pure rvalue**, purely rvalue, either purely literal,
|
||||||
such as `10`, `true`; either the result of the evaluation is equivalent to
|
such as `10`, `true`; either the result of the evaluation is equivalent to
|
||||||
a literal or anonymous temporary object, for example `1+2`.
|
a literal or anonymous temporary object, for example `1+2`.
|
||||||
Temporary variables returned by non-references, temporary variables generated
|
Temporary variables returned by non-references, temporary variables generated
|
||||||
by operation expressions, original literals, and Lambda expressions
|
by operation expressions, original literals, and Lambda expressions
|
||||||
are all pure rvalue values.
|
are all pure rvalue values.
|
||||||
|
|
||||||
**xvalue, expiring value** is the concept proposed by C++11 to introduce
|
Note that a literal (except a string literal) is a prvalue. However, a string
|
||||||
rvalue references (so in traditional C++, pure rvalue and rvalue are the same concept),
|
literal is an lvalue with type `const char` array. Consider the following examples:
|
||||||
that is, A value that is destroyed but can be moved.
|
|
||||||
|
|
||||||
It would be a little hard to understand the xvalue,
|
```cpp
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Correct. The type of "01234" is const char [6], so it is an lvalue
|
||||||
|
const char (&left)[6] = "01234";
|
||||||
|
|
||||||
|
// Assert success. It is a const char [6] indeed. Note that decltype(expr)
|
||||||
|
// yields lvalue reference if expr is an lvalue and neither an unparenthesized
|
||||||
|
// id-expression nor an unparenthesized class member access expression.
|
||||||
|
static_assert(std::is_same<decltype("01234"), const char(&)[6]>::value, "");
|
||||||
|
|
||||||
|
// Error. "01234" is an lvalue, which cannot be referenced by an rvalue reference
|
||||||
|
// const char (&&right)[6] = "01234";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, an array can be implicitly converted to a corresponding pointer.The result, if not an lvalue reference, is an rvalue (xvalue if the result is an rvalue reference, prvalue otherwise):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const char* p = "01234"; // Correct. "01234" is implicitly converted to const char*
|
||||||
|
const char*&& pr = "01234"; // Correct. "01234" is implicitly converted to const char*, which is a prvalue.
|
||||||
|
// const char*& pl = "01234"; // Error. There is no type const char* lvalue
|
||||||
|
```
|
||||||
|
|
||||||
|
**xvalue, expiring value** is the concept proposed by C++11 to introduce
|
||||||
|
rvalue references (so in traditional C++, pure rvalue and rvalue are the same concepts),
|
||||||
|
a value that is destroyed but can be moved.
|
||||||
|
|
||||||
|
It would be a little hard to understand the xvalue,
|
||||||
let's look at the code like this:
|
let's look at the code like this:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -276,33 +303,33 @@ std::vector<int> foo() {
|
|||||||
std::vector<int> v = foo();
|
std::vector<int> v = foo();
|
||||||
```
|
```
|
||||||
|
|
||||||
In such code, as far as the traditional understanding is concerned,
|
In such code, as far as the traditional understanding is concerned,
|
||||||
the return value `temp` of the function `foo` is internally created
|
the return value `temp` of the function `foo` is internally created
|
||||||
and then assigned to `v`, whereas when `v` gets this object, the entire `temp` is copied.
|
and then assigned to `v`, whereas when `v` gets this object, the entire `temp` is copied.
|
||||||
And then destroy `temp`, if this `temp` is very large, this will cause a lot of extra
|
And then destroy `temp`, if this `temp` is very large, this will cause a lot of extra
|
||||||
overhead (this is the problem that traditional C++ has been criticized for).
|
overhead (this is the problem that traditional C++ has been criticized for).
|
||||||
In the last line, `v` is the lvalue, and the value returned by `foo()` is
|
In the last line, `v` is the lvalue, and the value returned by `foo()` is
|
||||||
the rvalue (which is also a pure rvalue).
|
the rvalue (which is also a pure rvalue).
|
||||||
However, `v` can be caught by other variables, and the return value generated
|
|
||||||
by `foo()` is used as a temporary value. Once copied by `v`,
|
|
||||||
it will be destroyed immediately, and cannot be obtained or modified.
|
|
||||||
The xvalue defines an behavior in which temporary values can be identified
|
|
||||||
while being able to be moved.
|
|
||||||
|
|
||||||
After C++11, the compiler did some work for us, where the lvalue `temp`
|
However, `v` can be caught by other variables, and the return value generated by `foo()`
|
||||||
is subjected to this implicit rvalue conversion,
|
is used as a temporary value. Once copied by `v`, it will be destroyed immediately, and
|
||||||
equivalent to `static_cast<std::vector<int> &&>(temp)`,
|
cannot be obtained or modified. The xvalue defines behavior in which temporary values can be
|
||||||
where `v` here moves the value returned by `foo` locally.
|
identified while being able to be moved.
|
||||||
|
|
||||||
|
After C++11, the compiler did some work for us, where the lvalue `temp`
|
||||||
|
is subjected to this implicit rvalue conversion,
|
||||||
|
equivalent to `static_cast<std::vector<int> &&>(temp)`,
|
||||||
|
where `v` here moves the value returned by `foo` locally.
|
||||||
This is the move semantics we will mention later.
|
This is the move semantics we will mention later.
|
||||||
|
|
||||||
### rvalue reference and lvalue reference
|
### rvalue reference and lvalue reference
|
||||||
|
|
||||||
To get a xvalue, you need to use the declaration of the rvalue reference: `T &&`,
|
To get a xvalue, you need to use the declaration of the rvalue reference: `T &&`,
|
||||||
where `T` is the type.
|
where `T` is the type.
|
||||||
The statement of the rvalue reference extends the lifecycle of this temporary value,
|
The statement of the rvalue reference extends the lifecycle of this temporary value,
|
||||||
and as long as the variable is alive, the xvalue will continue to survive.
|
and as long as the variable is alive, the xvalue will continue to survive.
|
||||||
|
|
||||||
C++11 provides the `std::move` method to unconditionally convert
|
C++11 provides the `std::move` method to unconditionally convert
|
||||||
lvalue parameters to rvalues.
|
lvalue parameters to rvalues.
|
||||||
With it we can easily get a rvalue temporary object, for example:
|
With it we can easily get a rvalue temporary object, for example:
|
||||||
|
|
||||||
@@ -320,28 +347,29 @@ void reference(std::string&& str) {
|
|||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
std::string lv1 = "string,"; // lv1 is a lvalue
|
std::string lv1 = "string,"; // lv1 is a lvalue
|
||||||
// std::string&& r1 = s1; // illegal, rvalue can't ref to lvalue
|
// std::string&& r1 = lv1; // illegal, rvalue can't ref to lvalue
|
||||||
std::string&& rv1 = std::move(lv1); // legal, std::move can convert lvalue to rvalue
|
std::string&& rv1 = std::move(lv1); // legal, std::move can convert lvalue to rvalue
|
||||||
std::cout << rv1 << std::endl; // string,
|
std::cout << rv1 << std::endl; // string,
|
||||||
|
|
||||||
const std::string& lv2 = lv1 + lv1; // legal, const lvalue reference can extend temp variable's lifecycle
|
const std::string& lv2 = lv1 + lv1; // legal, const lvalue reference can
|
||||||
|
// extend temp variable's lifecycle
|
||||||
// lv2 += "Test"; // illegal, const ref can't be modified
|
// lv2 += "Test"; // illegal, const ref can't be modified
|
||||||
std::cout << lv2 << std::endl; // string,string
|
std::cout << lv2 << std::endl; // string,string,
|
||||||
|
|
||||||
std::string&& rv2 = lv1 + lv2; // legal, rvalue ref extend lifecycle
|
std::string&& rv2 = lv1 + lv2; // legal, rvalue ref extend lifecycle
|
||||||
rv2 += "string"; // legal, non-const reference can be modified
|
rv2 += "string"; // legal, non-const reference can be modified
|
||||||
std::cout << rv2 << std::endl; // string,string,string,
|
std::cout << rv2 << std::endl; // string,string,string,string
|
||||||
|
|
||||||
reference(rv2); // output: lvalue
|
reference(rv2); // output: lvalue
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`rv2` refers to an rvalue, but since it is a reference,
|
`rv2` refers to an rvalue, but since it is a reference,
|
||||||
`rv2` is still an lvalue.
|
`rv2` is still an lvalue.
|
||||||
|
|
||||||
Note that there is a very interesting historical issue here,
|
Note that there is a very interesting historical issue here,
|
||||||
let's look at the following code:
|
let's look at the following code:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -355,7 +383,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The first question, why not allow non-linear references to bind to non-lvalues?
|
The first question, why not allow non-constant references to bind to non-lvalues?
|
||||||
This is because there is a logic error in this approach:
|
This is because there is a logic error in this approach:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -368,28 +396,28 @@ void foo() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Since `int&` can't reference a parameter of type `double`,
|
Since `int&` can't reference a parameter of type `double`,
|
||||||
you must generate a temporary value to hold the value of `s`.
|
you must generate a temporary value to hold the value of `s`.
|
||||||
Thus, when `increase()` modifies this temporary value,
|
Thus, when `increase()` modifies this temporary value,
|
||||||
`s` itself is not modified after the call is completed.
|
`s` itself is not modified after the call is completed.
|
||||||
|
|
||||||
The second question, why do constant references allow binding to non-lvalues?
|
The second question, why do constant references allow binding to non-lvalues?
|
||||||
The reason is simple because Fortran needs it.
|
The reason is simple because Fortran needs it.
|
||||||
|
|
||||||
### Move semantics
|
### Move semantics
|
||||||
|
|
||||||
Traditional C++ has designed the concept of copy/copy for class objects
|
Traditional C++ has designed the concept of copy/copy for class objects
|
||||||
through copy constructors and assignment operators,
|
through copy constructors and assignment operators,
|
||||||
but in order to implement the movement of resources,
|
but to implement the movement of resources,
|
||||||
The caller must use the method of copying and then destructing first,
|
The caller must use the method of copying and then destructing first,
|
||||||
otherwise you need to implement the interface of the mobile object yourself.
|
otherwise, you need to implement the interface of the mobile object yourself.
|
||||||
Imagine moving to move your home directly to your new home instead of
|
Imagine moving your home directly to your new home instead of
|
||||||
copying everything (rebuy) to your new home.
|
copying everything (rebuy) to your new home.
|
||||||
Throwing away (destroying) all the original things is a very anti-human thing.
|
Throwing away (destroying) all the original things is a very anti-human thing.
|
||||||
|
|
||||||
Traditional C++ does not distinguish between the concepts of "mobile" and "copy",
|
Traditional C++ does not distinguish between the concepts of "mobile" and "copy",
|
||||||
resulting in a large amount of data copying, wasting time and space.
|
resulting in a large amount of data copying, wasting time and space.
|
||||||
The appearance of rvalue references solves the confusion of these two concepts,
|
The appearance of rvalue references solves the confusion of these two concepts,
|
||||||
for example:
|
for example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -397,19 +425,19 @@ for example:
|
|||||||
class A {
|
class A {
|
||||||
public:
|
public:
|
||||||
int *pointer;
|
int *pointer;
|
||||||
A():pointer(new int(1)) {
|
A():pointer(new int(1)) {
|
||||||
std::cout << "construct" << pointer << std::endl;
|
std::cout << "construct" << pointer << std::endl;
|
||||||
}
|
}
|
||||||
A(A& a):pointer(new int(*a.pointer)) {
|
A(A& a):pointer(new int(*a.pointer)) {
|
||||||
std::cout << "copy" << pointer << std::endl;
|
std::cout << "copy" << pointer << std::endl;
|
||||||
} // meaningless object copy
|
} // meaningless object copy
|
||||||
A(A&& a):pointer(a.pointer) {
|
A(A&& a):pointer(a.pointer) {
|
||||||
a.pointer = nullptr;
|
a.pointer = nullptr;
|
||||||
std::cout << "move" << pointer << std::endl;
|
std::cout << "move" << pointer << std::endl;
|
||||||
}
|
}
|
||||||
~A(){
|
~A(){
|
||||||
std::cout << "destruct" << pointer << std::endl;
|
std::cout << "destruct" << pointer << std::endl;
|
||||||
delete pointer;
|
delete pointer;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// avoid compiler optimization
|
// avoid compiler optimization
|
||||||
@@ -432,7 +460,7 @@ In the code above:
|
|||||||
1. First construct two `A` objects inside `return_rvalue`, and get the output of the two constructors;
|
1. First construct two `A` objects inside `return_rvalue`, and get the output of the two constructors;
|
||||||
2. After the function returns, it will generate a xvalue, which is referenced by the moving structure of `A` (`A(A&&)`), thus extending the life cycle, and taking the pointer in the rvalue and saving it to `obj`. In the middle, the pointer to the xvalue is set to `nullptr`, which prevents the memory area from being destroyed.
|
2. After the function returns, it will generate a xvalue, which is referenced by the moving structure of `A` (`A(A&&)`), thus extending the life cycle, and taking the pointer in the rvalue and saving it to `obj`. In the middle, the pointer to the xvalue is set to `nullptr`, which prevents the memory area from being destroyed.
|
||||||
|
|
||||||
This avoids meaningless copy constructs and enhances performance.
|
This avoids meaningless copy constructs and enhances performance.
|
||||||
Let's take a look at an example involving a standard library:
|
Let's take a look at an example involving a standard library:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -442,28 +470,29 @@ Let's take a look at an example involving a standard library:
|
|||||||
#include <string> // std::string
|
#include <string> // std::string
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
std::string str = "Hello world.";
|
std::string str = "Hello world.";
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
// use push_back(const T&), copy
|
// use push_back(const T&), copy
|
||||||
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;
|
||||||
|
|
||||||
// use push_back(const T&&), no copy
|
// use push_back(const T&&),
|
||||||
// the string will be moved to vector, and therefore std::move can reduce copy cost
|
// no copy the string will be moved to vector,
|
||||||
|
// and therefore std::move can reduce copy cost
|
||||||
v.push_back(std::move(str));
|
v.push_back(std::move(str));
|
||||||
// str is empty now
|
// str is empty now
|
||||||
std::cout << "str: " << str << std::endl;
|
std::cout << "str: " << str << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Perfect forwarding
|
### Perfect forwarding
|
||||||
|
|
||||||
As we mentioned earlier, the rvalue reference of a declaration is actually an lvalue.
|
As we mentioned earlier, the rvalue reference of a declaration is actually an lvalue.
|
||||||
This creates problems for us to parameterize (pass):
|
This creates problems for us to parameterize (pass):
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -496,22 +525,22 @@ For `pass(1)`, although the value is the rvalue, since `v` is a reference, it is
|
|||||||
Therefore `reference(v)` will call `reference(int&)` and output lvalue.
|
Therefore `reference(v)` will call `reference(int&)` and output lvalue.
|
||||||
For `pass(l)`, `l` is an lvalue, why is it successfully passed to `pass(T&&)`?
|
For `pass(l)`, `l` is an lvalue, why is it successfully passed to `pass(T&&)`?
|
||||||
|
|
||||||
This is based on the **reference contraction rule**: In traditional C++, we are not able to continue to reference a reference type.
|
This is based on the **reference collapsing rule**: In traditional C++, we are not able to continue to reference a reference type.
|
||||||
However,
|
However,
|
||||||
C++ has relaxed this practice with the advent of rvalue references,
|
C++ has relaxed this practice with the advent of rvalue references,
|
||||||
resulting in a reference collapse rule that allows us to reference references,
|
resulting in a reference collapse rule that allows us to reference references,
|
||||||
both lvalue and rvalue. But follow the rules below:
|
both lvalue and rvalue. But follow the rules below:
|
||||||
|
|
||||||
| Function parameter type | Argument parameter type | Post-derivation function parameter type |
|
| Function parameter type | Argument parameter type | Post-derivation function parameter type |
|
||||||
| :--------: | :--------: | :-------------: |
|
| :---------------------: | :---------------------: | :-------------------------------------: |
|
||||||
| T& | lvalue ref | T& |
|
| T& | lvalue ref | T& |
|
||||||
| T& | rvalue ref | T& |
|
| T& | rvalue ref | T& |
|
||||||
| T&& | lvalue ref | T& |
|
| T&& | lvalue ref | T& |
|
||||||
| T&& | rvalue ref | T&& |
|
| T&& | rvalue ref | T&& |
|
||||||
|
|
||||||
Therefore, the use of `T&&` in a template function may not be able to make an rvalue reference, and when a lvalue is passed, a reference to this function will be derived as an lvalue.
|
Therefore, the use of `T&&` in a template function may not be able to make an rvalue reference, and when a lvalue is passed, a reference to this function will be derived as an lvalue.
|
||||||
More precisely, ** no matter what type of reference the template parameter is, the template parameter can be derived as a right reference type** if and only if the argument type is a right reference.
|
More precisely, **no matter what type of reference the template parameter is, the template parameter can be derived as a right reference type** if and only if the argument type is a right reference.
|
||||||
This makes `v` a successful delivery of lvalues.
|
This makes `v` successful delivery of lvalues.
|
||||||
|
|
||||||
Perfect forwarding is based on the above rules. The so-called perfect forwarding is to let us pass the parameters,
|
Perfect forwarding is based on the above rules. The so-called perfect forwarding is to let us pass the parameters,
|
||||||
Keep the original parameter type (lvalue reference keeps lvalue reference, rvalue reference keeps rvalue reference).
|
Keep the original parameter type (lvalue reference keeps lvalue reference, rvalue reference keeps rvalue reference).
|
||||||
@@ -540,11 +569,11 @@ void pass(T&& v) {
|
|||||||
int main() {
|
int main() {
|
||||||
std::cout << "rvalue pass:" << std::endl;
|
std::cout << "rvalue pass:" << std::endl;
|
||||||
pass(1);
|
pass(1);
|
||||||
|
|
||||||
std::cout << "lvalue pass:" << std::endl;
|
std::cout << "lvalue pass:" << std::endl;
|
||||||
int l = 1;
|
int l = 1;
|
||||||
pass(l);
|
pass(l);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -567,11 +596,11 @@ static_cast<T&&> param passing: lvalue reference
|
|||||||
Regardless of whether the pass parameter is an lvalue or an rvalue, the normal pass argument will forward the argument as an lvalue.
|
Regardless of whether the pass parameter is an lvalue or an rvalue, the normal pass argument will forward the argument as an lvalue.
|
||||||
So `std::move` will always accept an lvalue, which forwards the call to `reference(int&&)` to output the rvalue reference.
|
So `std::move` will always accept an lvalue, which forwards the call to `reference(int&&)` to output the rvalue reference.
|
||||||
|
|
||||||
Only `std::forward` does not cause any extra copies, and ** perfectly forwards ** (passes) the arguments of the function to other functions that are called internally.
|
Only `std::forward` does not cause any extra copies and **perfectly forwards** (passes) the arguments of the function to other functions that are called internally.
|
||||||
|
|
||||||
`std::forward` is the same as `std::move`, and nothing is done. `std::move` simply converts the lvalue to the rvalue.
|
`std::forward` is the same as `std::move`, and nothing is done. `std::move` simply converts the lvalue to the rvalue.
|
||||||
`std::forward` is just a simple conversion of the parameters. From the point of view of the phenomenon,
|
`std::forward` is just a simple conversion of the parameters. From the point of view of the phenomenon,
|
||||||
`std::forward<T>(v)` is exactly the same as `static_cast<T&&>(v)`.
|
`std::forward<T>(v)` is the same as `static_cast<T&&>(v)`.
|
||||||
|
|
||||||
Readers may be curious as to why a statement can return values for two types of returns.
|
Readers may be curious as to why a statement can return values for two types of returns.
|
||||||
Let's take a quick look at the concrete implementation of `std::forward`. `std::forward` contains two overloads:
|
Let's take a quick look at the concrete implementation of `std::forward`. `std::forward` contains two overloads:
|
||||||
@@ -591,14 +620,14 @@ constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcep
|
|||||||
```
|
```
|
||||||
|
|
||||||
In this implementation, the function of `std::remove_reference` is to eliminate references in the type.
|
In this implementation, the function of `std::remove_reference` is to eliminate references in the type.
|
||||||
And `std::is_lvalue_reference` is used to check if the type derivation is correct, in the second implementation of `std::forward`
|
And `std::is_lvalue_reference` is used to check if the type derivation is correct, in the second implementation of `std::forward`.
|
||||||
Check that the received value is indeed an lvalue, which in turn reflects the collapse rule.
|
Check that the received value is indeed an lvalue, which in turn reflects the collapse rule.
|
||||||
|
|
||||||
When `std::forward` accepts an lvalue, `_Tp` is deduced to the lvalue, so the return value is the lvalue; and when it accepts the rvalue,
|
When `std::forward` accepts an lvalue, `_Tp` is deduced to the lvalue, so the return value is the lvalue; and when it accepts the rvalue,
|
||||||
`_Tp` is derived as an rvalue reference, and based on the collapse rule, the return value becomes the rvalue of `&& + &&`.
|
`_Tp` is derived as an rvalue reference, and based on the collapse rule, the return value becomes the rvalue of `&& + &&`.
|
||||||
It can be seen that the principle of `std::forward` is to make clever use of the differences in template type derivation.
|
It can be seen that the principle of `std::forward` is to make clever use of the differences in template type derivation.
|
||||||
|
|
||||||
At this point we can answer the question: Why is `auto&&` the safest way to use looping statements?
|
At this point, we can answer the question: Why is `auto&&` the safest way to use looping statements?
|
||||||
Because when `auto` is pushed to a different lvalue and rvalue reference, the collapsed combination with `&&` is perfectly forwarded.
|
Because when `auto` is pushed to a different lvalue and rvalue reference, the collapsed combination with `&&` is perfectly forwarded.
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
@@ -606,6 +635,7 @@ Because when `auto` is pushed to a different lvalue and rvalue reference, the co
|
|||||||
This chapter introduces the most important runtime enhancements in modern C++, and I believe that all the features mentioned in this section are worth knowing:
|
This chapter introduces the most important runtime enhancements in modern C++, and I believe that all the features mentioned in this section are worth knowing:
|
||||||
|
|
||||||
Lambda expression
|
Lambda expression
|
||||||
|
|
||||||
1. Function object container std::function
|
1. Function object container std::function
|
||||||
2. rvalue reference
|
2. rvalue reference
|
||||||
|
|
||||||
@@ -617,4 +647,4 @@ Lambda expression
|
|||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ order: 4
|
|||||||
|
|
||||||
### `std::array`
|
### `std::array`
|
||||||
|
|
||||||
When you see this container, you will definitely have this problem:
|
When you see this container, you will have this problem:
|
||||||
|
|
||||||
1. Why introduce `std::array` instead of `std::vector` directly?
|
1. Why introduce `std::array` instead of `std::vector` directly?
|
||||||
2. Already have a traditional array, why use `std::array`?
|
2. Already have a traditional array, why use `std::array`?
|
||||||
|
|
||||||
First answer the first question. Unlike `std::vector`, the size of the `std::array` object is fixed. If the container size is fixed, then the `std::array` container can be used first.
|
First, answer the first question. Unlike `std::vector`, the size of the `std::array` object is fixed. If the container size is fixed, then the `std::array` container can be used first.
|
||||||
In addition, since `std::vector` is automatically expanded, when a large amount of data is stored, and the container is deleted,
|
Also, since `std::vector` is automatically expanded, when a large amount of data is stored, and the container is deleted,
|
||||||
The container does not automatically return the corresponding memory of the deleted element. In this case, you need to manually run `shrink_to_fit()` to release this part of the memory.
|
The container does not automatically return the corresponding memory of the deleted element. In this case, you need to manually run `shrink_to_fit()` to release this part of the memory.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -26,9 +26,9 @@ std::vector<int> v;
|
|||||||
std::cout << "size:" << v.size() << std::endl; // output 0
|
std::cout << "size:" << v.size() << std::endl; // output 0
|
||||||
std::cout << "capacity:" << v.capacity() << std::endl; // output 0
|
std::cout << "capacity:" << v.capacity() << std::endl; // output 0
|
||||||
|
|
||||||
// As you can see, the storage of std::vector is automatically managed and
|
// As you can see, the storage of std::vector is automatically managed and
|
||||||
// automatically expanded as needed.
|
// automatically expanded as needed.
|
||||||
// But if there is not enough space, you need to redistribute more memory,
|
// But if there is not enough space, you need to redistribute more memory,
|
||||||
// and reallocating memory is usually a performance-intensive operation.
|
// and reallocating memory is usually a performance-intensive operation.
|
||||||
v.push_back(1);
|
v.push_back(1);
|
||||||
v.push_back(2);
|
v.push_back(2);
|
||||||
@@ -42,9 +42,9 @@ v.push_back(5);
|
|||||||
std::cout << "size:" << v.size() << std::endl; // output 5
|
std::cout << "size:" << v.size() << std::endl; // output 5
|
||||||
std::cout << "capacity:" << v.capacity() << std::endl; // output 8
|
std::cout << "capacity:" << v.capacity() << std::endl; // output 8
|
||||||
|
|
||||||
// As can be seen below, although the container empties the element,
|
// As can be seen below, although the container empties the element,
|
||||||
// the memory of the emptied element is not returned.
|
// the memory of the emptied element is not returned.
|
||||||
v.clear();
|
v.clear();
|
||||||
std::cout << "size:" << v.size() << std::endl; // output 0
|
std::cout << "size:" << v.size() << std::endl; // output 0
|
||||||
std::cout << "capacity:" << v.capacity() << std::endl; // output 8
|
std::cout << "capacity:" << v.capacity() << std::endl; // output 8
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ std::sort(arr.begin(), arr.end());
|
|||||||
|
|
||||||
### `std::forward_list`
|
### `std::forward_list`
|
||||||
|
|
||||||
`std::forward_list` is a list container, and the usage is basically similar to `std::list`, so we don't spend a lot of time introducing it.
|
`std::forward_list` is a list container, and the usage is similar to `std::list`, so we don't spend a lot of time introducing it.
|
||||||
|
|
||||||
Need to know is that, unlike the implementation of the doubly linked list of `std::list`, `std::forward_list` is implemented using a singly linked list.
|
Need to know is that, unlike the implementation of the doubly linked list of `std::list`, `std::forward_list` is implemented using a singly linked list.
|
||||||
Provides element insertion of `O(1)` complexity, does not support fast random access (this is also a feature of linked lists),
|
Provides element insertion of `O(1)` complexity, does not support fast random access (this is also a feature of linked lists),
|
||||||
@@ -115,10 +115,10 @@ We are already familiar with the ordered container `std::map`/`std::set` in trad
|
|||||||
The average complexity of inserts and searches is `O(log(size))`. When inserting an element, the element size is compared according to the `<` operator and the element is determined to be the same.
|
The average complexity of inserts and searches is `O(log(size))`. When inserting an element, the element size is compared according to the `<` operator and the element is determined to be the same.
|
||||||
And select the appropriate location to insert into the container. When traversing the elements in this container, the output will be traversed one by one in the order of the `<` operator.
|
And select the appropriate location to insert into the container. When traversing the elements in this container, the output will be traversed one by one in the order of the `<` operator.
|
||||||
|
|
||||||
The elements in the unordered container are not sorted, and the internals are implemented by the Hash table. The average complexity of inserting and searching for elements is `O(constant)`,
|
The elements in the unordered container are not sorted, and the internals is implemented by the Hash table. The average complexity of inserting and searching for elements is `O(constant)`,
|
||||||
Significant performance gains can be achieved without concern for the order of the elements inside the container.
|
Significant performance gains can be achieved without concern for the order of the elements inside the container.
|
||||||
|
|
||||||
C++11 introduces two sets of unordered containers: `std::unordered_map`/`std::unordered_multimap` and
|
C++11 introduces two unordered containers: `std::unordered_map`/`std::unordered_multimap` and
|
||||||
`std::unordered_set`/`std::unordered_multiset`.
|
`std::unordered_set`/`std::unordered_multiset`.
|
||||||
|
|
||||||
Their usage is basically similar to the original `std::map`/`std::multimap`/`std::set`/`set::multiset`
|
Their usage is basically similar to the original `std::map`/`std::multimap`/`std::set`/`set::multiset`
|
||||||
@@ -142,12 +142,12 @@ int main() {
|
|||||||
{3, "3"},
|
{3, "3"},
|
||||||
{2, "2"}
|
{2, "2"}
|
||||||
};
|
};
|
||||||
|
|
||||||
// iterates in the same way
|
// iterates in the same way
|
||||||
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)
|
||||||
@@ -172,7 +172,7 @@ Key:[3] Value:[3]
|
|||||||
## 4.3 Tuples
|
## 4.3 Tuples
|
||||||
|
|
||||||
Programmers who have known Python should be aware of the concept of tuples. Looking at the containers in traditional C++, except for `std::pair`
|
Programmers who have known Python should be aware of the concept of tuples. Looking at the containers in traditional C++, except for `std::pair`
|
||||||
There seems to be no ready-made structure to store different types of data (usually we will define the structure ourselves).
|
there seems to be no ready-made structure to store different types of data (usually we will define the structure ourselves).
|
||||||
But the flaw of `std::pair` is obvious, only two elements can be saved.
|
But the flaw of `std::pair` is obvious, only two elements can be saved.
|
||||||
|
|
||||||
### Basic Operations
|
### Basic Operations
|
||||||
@@ -256,8 +256,8 @@ constexpr std::variant<T...> tuple_index(const std::tuple<T...>& tpl, size_t i)
|
|||||||
return _tuple_index<0>(tpl, i);
|
return _tuple_index<0>(tpl, i);
|
||||||
}
|
}
|
||||||
template <typename T0, typename ... Ts>
|
template <typename T0, typename ... Ts>
|
||||||
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
|
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
|
||||||
std::visit([&](auto && x){ s << x;}, v);
|
std::visit([&](auto && x){ s << x;}, v);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -278,7 +278,7 @@ auto new_tuple = std::tuple_cat(get_student(1), std::move(t));
|
|||||||
```
|
```
|
||||||
|
|
||||||
You can immediately see how quickly you can traverse a tuple? But we just introduced how to index a `tuple` by a very number at runtime, then the traversal becomes simpler.
|
You can immediately see how quickly you can traverse a tuple? But we just introduced how to index a `tuple` by a very number at runtime, then the traversal becomes simpler.
|
||||||
First we need to know the length of a tuple, which can:
|
First, we need to know the length of a tuple, which can:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -292,12 +292,12 @@ This will iterate over the tuple:
|
|||||||
```cpp
|
```cpp
|
||||||
for(int i = 0; i != tuple_len(new_tuple); ++i)
|
for(int i = 0; i != tuple_len(new_tuple); ++i)
|
||||||
// runtime indexing
|
// runtime indexing
|
||||||
std::cout << tuple_index(i, new_tuple) << std::endl;
|
std::cout << tuple_index(new_tuple, i) << std::endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
This chapter briefly introduces the new containers in modern C++. Their usage is similar to that of the existing containers in C++. It is relatively simple, and you can choose the containers you need to use according to the actual scene, so as to get better performance.
|
This chapter briefly introduces the new containers in modern C++. Their usage is similar to that of the existing containers in C++. It is relatively simple, and you can choose the containers you need to use according to the actual scene, to get better performance.
|
||||||
|
|
||||||
Although `std::tuple` is effective, the standard library provides limited functionality and there is no way to meet the requirements of runtime indexing and iteration. Fortunately, we have other methods that we can implement on our own.
|
Although `std::tuple` is effective, the standard library provides limited functionality and there is no way to meet the requirements of runtime indexing and iteration. Fortunately, we have other methods that we can implement on our own.
|
||||||
|
|
||||||
@@ -305,4 +305,4 @@ Although `std::tuple` is effective, the standard library provides limited functi
|
|||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -18,15 +18,15 @@ In traditional C++, "remembering" to manually release resources is not always a
|
|||||||
So the usual practice is that for an object, we apply for space when constructor, and free space when the destructor (called when leaving the scope).
|
So the usual practice is that for an object, we apply for space when constructor, and free space when the destructor (called when leaving the scope).
|
||||||
That is, we often say that the RAII resource acquisition is the initialization technology.
|
That is, we often say that the RAII resource acquisition is the initialization technology.
|
||||||
|
|
||||||
There are exceptions to everything, we always have the need to allocate objects on free storage. In traditional C++ we have to use `new` and `delete` to "remember" to release resources. C++11 introduces the concept of smart pointers, using the idea of reference counting, so that programmers no longer need to care about manually releasing memory.
|
There are exceptions to everything, we always need to allocate objects on free storage. In traditional C++ we have to use `new` and `delete` to "remember" to release resources. C++11 introduces the concept of smart pointers, using the idea of reference counting so that programmers no longer need to care about manually releasing memory.
|
||||||
These smart pointers include `std::shared_ptr`/`std::unique_ptr`/`std::weak_ptr`, which need to include the header file `<memory>`.
|
These smart pointers include `std::shared_ptr`/`std::unique_ptr`/`std::weak_ptr`, which need to include the header file `<memory>`.
|
||||||
|
|
||||||
> Note: The reference count is not garbage collection. The reference count can recover the objects that are no longer used as soon as possible, and will not cause long waits during the recycling process.
|
> Note: The reference count is not garbage collection. The reference count can recover the objects that are no longer used as soon as possible, and will not cause long waits during the recycling process.
|
||||||
> More clearly and clearly indicate the life cycle of resources.
|
> More clearly and indicate the life cycle of resources.
|
||||||
|
|
||||||
## 5.2 `std::shared_ptr`
|
## 5.2 `std::shared_ptr`
|
||||||
|
|
||||||
`std::shared_ptr` is a smart pointer that records how many `shared_ptr` points to an object, eliminating the display call `delete`, which automatically deletes the object when the reference count becomes zero.
|
`std::shared_ptr` is a smart pointer that records how many `shared_ptr` points to an object, eliminating to call `delete`, which automatically deletes the object when the reference count becomes zero.
|
||||||
|
|
||||||
But not enough, because using `std::shared_ptr` still needs to be called with `new`, which makes the code a certain degree of asymmetry.
|
But not enough, because using `std::shared_ptr` still needs to be called with `new`, which makes the code a certain degree of asymmetry.
|
||||||
|
|
||||||
@@ -36,12 +36,10 @@ And return the `std::shared_ptr` pointer of this object type. For example:
|
|||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#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); // illegal, no direct assignment
|
// auto pointer = new int(10); // illegal, no direct assignment
|
||||||
// Constructed a std::shared_ptr
|
// Constructed a std::shared_ptr
|
||||||
auto pointer = std::make_shared<int>(10);
|
auto pointer = std::make_shared<int>(10);
|
||||||
@@ -59,21 +57,27 @@ And see the reference count of an object by `use_count()`. E.g:
|
|||||||
auto pointer = std::make_shared<int>(10);
|
auto pointer = std::make_shared<int>(10);
|
||||||
auto pointer2 = pointer; // reference count+1
|
auto pointer2 = pointer; // reference count+1
|
||||||
auto pointer3 = pointer; // reference count+1
|
auto pointer3 = pointer; // reference count+1
|
||||||
int *p = pointer.get(); // no increase of reference count
|
int *p = pointer.get(); // no increase of reference count
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
|
|
||||||
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.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; // 3
|
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
|
||||||
|
|
||||||
pointer2.reset();
|
pointer2.reset();
|
||||||
std::cout << "reset pointer2:" << std::endl;
|
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; // 0, pointer2 has reset
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
|
||||||
|
std::cout << "pointer2.use_count() = "
|
||||||
|
<< pointer2.use_count() << std::endl; // pointer2 has reset, 0
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
|
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
|
||||||
|
|
||||||
pointer3.reset();
|
pointer3.reset();
|
||||||
std::cout << "reset pointer3:" << std::endl;
|
std::cout << "reset pointer3:" << std::endl;
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
|
|
||||||
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
|
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 has reset
|
std::cout << "pointer3.use_count() = "
|
||||||
|
<< pointer3.use_count() << std::endl; // pointer3 has reset, 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5.3 `std::unique_ptr`
|
## 5.3 `std::unique_ptr`
|
||||||
@@ -81,12 +85,12 @@ std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; //
|
|||||||
`std::unique_ptr` is an exclusive smart pointer that prohibits other smart pointers from sharing the same object, thus keeping the code safe:
|
`std::unique_ptr` is an exclusive smart pointer that prohibits other smart pointers from sharing the same object, thus keeping the code safe:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique was introduced in C++14
|
std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique, from C++14
|
||||||
std::unique_ptr<int> pointer2 = pointer; // illegal
|
std::unique_ptr<int> pointer2 = pointer; // illegal
|
||||||
```
|
```
|
||||||
|
|
||||||
> `make_unique` is not complicated. C++11 does not provide `std::make_unique`, which can be implemented by itself:
|
> `make_unique` is not complicated. C++11 does not provide `std::make_unique`, which can be implemented by itself:
|
||||||
>
|
>
|
||||||
> ```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 ) {
|
||||||
@@ -114,31 +118,31 @@ void f(const Foo &) {
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::unique_ptr<Foo> p1(std::make_unique<Foo>());
|
std::unique_ptr<Foo> p1(std::make_unique<Foo>());
|
||||||
|
|
||||||
// p1 is not empty, prints
|
// p1 is not empty, prints
|
||||||
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 is not empty, prints
|
// p2 is not empty, prints
|
||||||
f(*p2);
|
f(*p2);
|
||||||
|
|
||||||
// p2 is not empty, prints
|
// p2 is not empty, prints
|
||||||
if(p2) p2->foo();
|
if(p2) p2->foo();
|
||||||
|
|
||||||
// p1 is empty, no prints
|
// p1 is empty, no prints
|
||||||
if(p1) p1->foo();
|
if(p1) p1->foo();
|
||||||
|
|
||||||
p1 = std::move(p2);
|
p1 = std::move(p2);
|
||||||
|
|
||||||
// p2 is empty, no prints
|
// p2 is empty, no prints
|
||||||
if(p2) p2->foo();
|
if(p2) p2->foo();
|
||||||
std::cout << "p2 was destroied" << std::endl;
|
std::cout << "p2 was destroyed" << std::endl;
|
||||||
}
|
}
|
||||||
// p1 is not empty, prints
|
// p1 is not empty, prints
|
||||||
if (p1) p1->foo();
|
if (p1) p1->foo();
|
||||||
|
|
||||||
// Foo instance will be destroied when leaving the scope
|
// Foo instance will be destroyed when leaving the scope
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -157,14 +161,14 @@ class A {
|
|||||||
public:
|
public:
|
||||||
std::shared_ptr<B> pointer;
|
std::shared_ptr<B> pointer;
|
||||||
~A() {
|
~A() {
|
||||||
std::cout << "A was destroied" << std::endl;
|
std::cout << "A was destroyed" << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class B {
|
class B {
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<A> pointer;
|
std::shared_ptr<A> pointer;
|
||||||
~B() {
|
~B() {
|
||||||
std::cout << "B was destroied" << std::endl;
|
std::cout << "B was destroyed" << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
int main() {
|
int main() {
|
||||||
@@ -172,14 +176,14 @@ int main() {
|
|||||||
std::shared_ptr<B> b = std::make_shared<B>();
|
std::shared_ptr<B> b = std::make_shared<B>();
|
||||||
a->pointer = b;
|
a->pointer = b;
|
||||||
b->pointer = a;
|
b->pointer = a;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The result is that A and B will not be destroyed. This is because the pointer inside a, b also references `a, b`, which makes the reference count of `a, b` become 2, leaving the scope. When the `a, b` smart pointer is destructed, it can only cause the reference count of this area to be decremented by one. This causes the memory area reference count pointed to by the `a, b` object to be non-zero, but the external has no The way to find this area, it also caused a memory leak, as shown in Figure 5.1:
|
The result is that A and B will not be destroyed. This is because the pointer inside a, b also references `a, b`, which makes the reference count of `a, b` becomes 2, leaving the scope. When the `a, b` smart pointer is destructed, it can only cause the reference count of this area to be decremented by one. This causes the memory area reference count pointed to by the `a, b` object to be non-zero, but the external has no way to find this area, it also caused a memory leak, as shown in Figure 5.1:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The solution to this problem is to use the weak reference pointer `std::weak_ptr`, which is a weak reference (compared to `std::shared_ptr` is a strong reference). A weak reference does not cause an increase in the reference count. When a weak reference is used, the final release process is shown in Figure 5.2:
|
The solution to this problem is to use the weak reference pointer `std::weak_ptr`, which is a weak reference (compared to `std::shared_ptr` is a strong reference). A weak reference does not cause an increase in the reference count. When a weak reference is used, the final release process is shown in Figure 5.2:
|
||||||
|
|
||||||
@@ -187,7 +191,8 @@ The solution to this problem is to use the weak reference pointer `std::weak_ptr
|
|||||||
|
|
||||||
In the above figure, only B is left in the last step, and B does not have any smart pointers to reference it, so this memory resource will also be released.
|
In the above figure, only B is left in the last step, and B does not have any smart pointers to reference it, so this memory resource will also be released.
|
||||||
|
|
||||||
`std::weak_ptr` has no `*` operator and `->` operator, so it can't operate on resources. Its only function is to check if `std::shared_ptr` exists, its `expired() The ` method can return `true` when the resource is not released, otherwise it returns `false`.
|
`std::weak_ptr` has no implemented `*` and `->` operators, therefore it cannot operate on resources. `std::weak_ptr` allows us to check if a `std::shared_ptr` exists or not. The `expired()` method of a `std::weak_ptr` returns `false` when the resource is not released; Otherwise, it returns `true`.
|
||||||
|
Furthermore, it can also be used for the purpose of obtaining `std::shared_ptr`, which points to the original object. The `lock()` method returns a `std::shared_ptr` to the original object when the resource is not released, or `nullptr` otherwise.
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
@@ -197,8 +202,8 @@ The technology of smart pointers is not novel. It is a common technology in many
|
|||||||
|
|
||||||
## Further Readings
|
## Further Readings
|
||||||
|
|
||||||
- [Why does C++11 have `make_shared` but not `make_unique`](http://stackoverflow.com/questions/12580432/why-does-c11-have-make-shared-but-not-make-unique)
|
- [Why does C++11 have `make_shared` but not `make_unique`](https://stackoverflow.com/questions/12580432/why-does-c11-have-make-shared-but-not-make-unique)
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -10,64 +10,62 @@ order: 6
|
|||||||
|
|
||||||
## 6.1 Introduction
|
## 6.1 Introduction
|
||||||
|
|
||||||
Regular expressions are not part of the C++ language and therefore we only briefly
|
Regular expressions are not part of the C++ language and therefore we only briefly
|
||||||
introduced it here.
|
introduced it here.
|
||||||
|
|
||||||
Regular expressions describe a pattern of string matching.
|
Regular expressions describe a pattern of string matching.
|
||||||
The general use of regular expressions is mainly to achieve
|
The general use of regular expressions is mainly to achieve
|
||||||
the following three requirements:
|
the following three requirements:
|
||||||
|
|
||||||
1. Check if a string contains some form of substring;
|
1. Check if a string contains some form of substring;
|
||||||
2. Replace the matching substrings;
|
2. Replace the matching substrings;
|
||||||
3. Take the eligible substring from a string.
|
3. Take the eligible substring from a string.
|
||||||
|
|
||||||
Regular expressions are text patterns consisting of ordinary characters (such as a to z)
|
Regular expressions are text patterns consisting of ordinary characters (such as a to z)
|
||||||
and special characters. A pattern describes one or more strings to match when searching for text.
|
and special characters. A pattern describes one or more strings to match when searching for text.
|
||||||
Regular expressions act as a template to match a character pattern to the string being searched.
|
Regular expressions act as a template to match a character pattern to the string being searched.
|
||||||
|
|
||||||
### Ordinary characters
|
### Ordinary characters
|
||||||
|
|
||||||
Normal characters include all printable and unprintable characters that
|
Normal characters include all printable and unprintable characters that are not explicitly specified as metacharacters. This includes all uppercase
|
||||||
are not explicitly specified as metacharacters. This includes all uppercase
|
|
||||||
and lowercase letters, all numbers, all punctuation, and some other symbols.
|
and lowercase letters, all numbers, all punctuation, and some other symbols.
|
||||||
|
|
||||||
### Special characters
|
### Special characters
|
||||||
|
|
||||||
A special character is a character with special meaning in a regular expression,
|
A special character is a character with special meaning in a regular expression and is also the core matching syntax of a regular expression. See the table below:
|
||||||
and is also the core matching syntax of a regular expression. See the table below:
|
|
||||||
|
|
||||||
|Special characters|Description|
|
| Symbol | Description |
|
||||||
|:---:|:------------------------------------------------------|
|
|:----------------:|:---|
|
||||||
|`$`| Matches the end position of the input string. |
|
| `$` | Matches the end position of the input string.|
|
||||||
|`(`,`)`| Marks the start and end of a subexpression. Subexpressions can be obtained for later use. |
|
| `(`,`)` | Marks the start and end of a subexpression. Subexpressions can be obtained for later use.|
|
||||||
|`*`| Matches the previous subexpression zero or more times. |
|
| `*` | Matches the previous subexpression zero or more times. |
|
||||||
|`+`| Matches the previous subexpression one or more times. |
|
| `+` | Matches the previous subexpression one or more times.|
|
||||||
|`.`| Matches any single character except the newline character `\n`. |
|
| `.` | Matches any single character except the newline character `\n`.|
|
||||||
|`[`| Marks the beginning of a bracket expression. |
|
| `[` | Marks the beginning of a bracket expression.|
|
||||||
|`?`| Matches the previous subexpression zero or one time, or indicates a non-greedy qualifier. |
|
| `?` | Matches the previous subexpression zero or one time, or indicates a non-greedy qualifier.|
|
||||||
| `\`| Marks the next character as either a special character, or a literal character, or a backward reference, or an octal escape character. For example, `n` Matches the character `n`. `\n` matches newline characters. The sequence `\\` Matches the `'\'` character, while `\(` matches the `'('` character.|
|
| `\` | Marks the next character as either a special character, or a literal character, or a backward reference, or an octal escape character. For example, `n` Matches the character `n`. `\n` matches newline characters. The sequence `\\` Matches the `'\'` character, while `\(` matches the `'('` character. |
|
||||||
|`^`| Matches the beginning of the input string, unless it is used in a square bracket expression, at which point it indicates that the set of characters is not accepted. |
|
| `^` | Matches the beginning of the input string, unless it is used in a square bracket expression, at which point it indicates that the set of characters is not accepted.|
|
||||||
|`{`| Marks the beginning of a qualifier expression. |
|
| `{` | Marks the beginning of a qualifier expression.|
|
||||||
|`\`| Indicates a choice between the two. |
|
| `\|` | Indicates a choice between the two.|
|
||||||
|
|
||||||
### Quantifiers
|
### Quantifiers
|
||||||
|
|
||||||
The qualifier is used to specify how many times a given component of a regular expression must appear to satisfy the match. See the table below:
|
The qualifier is used to specify how many times a given component of a regular expression must appear to satisfy the match. See the table below:
|
||||||
|
|
||||||
|Character|Description|
|
| Symbol | Description |
|
||||||
|:---:|:------------------------------------------------------|
|
|:-------:|:-----|
|
||||||
|`*`| matches the previous subexpression zero or more times. For example, `foo*` matches `fo` and `foooo`. `*` is equivalent to `{0,}`. |
|
| `*` | matches the previous subexpression zero or more times. For example, `foo*` matches `fo` and `foooo`. `*` is equivalent to `{0,}`.|
|
||||||
|`+`| matches the previous subexpression one or more times. For example, `foo+` matches `foo` and `foooo` but does not match `fo`. `+` is equivalent to `{1,}`. |
|
| `+` | matches the previous subexpression one or more times. For example, `foo+` matches `foo` and `foooo` but does not match `fo`. `+` is equivalent to `{1,}`.|
|
||||||
|`?`| matches the previous subexpression zero or one time. For example, `Your(s)?` can match `Your` in `Your` or `Yours`. `?` is equivalent to `{0,1}`. |
|
| `?` | matches the previous subexpression zero or one time. For example, `Your(s)?` can match `Your` in `Your` or `Yours`. `?` is equivalent to `{0,1}`.|
|
||||||
|`{n}`| `n` is a non-negative integer. Matches the determined `n` times. For example, `o{2}` cannot match `o` in `for`, but can match two `o` in `foo`. |
|
| `{n}` | `n` is a non-negative integer. Matches the determined `n` times. For example, `o{2}` cannot match `o` in `for`, but can match two `o` in `foo`.|
|
||||||
|`{n,}`| `n` is a non-negative integer. Match at least `n` times. For example, `o{2,}` cannot match `o` in `for`, but matches all `o` in `foooooo`. `o{1,}` is equivalent to `o+`. `o{0,}` is equivalent to `o*`. |
|
| `{n,}` | `n` is a non-negative integer. Match at least `n` times. For example, `o{2,}` cannot match `o` in `for`, but matches all `o` in `foooooo`. `o{1,}` is equivalent to `o+`. `o{0,}` is equivalent to `o*`.|
|
||||||
|`{n,m}`| `m` and `n` are non-negative integers, where `n` is less than or equal to `m`. Matches at least `n` times and matches up to `m` times. For example, `o{1,3}` will match the first three `o` in `foooooo`. `o{0,1}` is equivalent to `o?`. Note that there can be no spaces between the comma and the two numbers. |
|
| `{n,m}` | `m` and `n` are non-negative integers, where `n` is less than or equal to `m`. Matches at least `n` times and matches up to `m` times. For example, `o{1,3}` will match the first three `o` in `foooooo`. `o{0,1}` is equivalent to `o?`. Note that there can be no spaces between the comma and the two numbers. |
|
||||||
|
|
||||||
With these two tables, we can usually read almost all regular expressions.
|
With these two tables, we can usually read almost all regular expressions.
|
||||||
|
|
||||||
## 6.2 `std::regex` and Its Related
|
## 6.2 `std::regex` and Its Related
|
||||||
|
|
||||||
The most common way to match string content is to use regular expressions. Unfortunately, in traditional C++, regular expressions have not been supported by the language level, and are not included in the standard library. C++ is a high-performance language. In the development of background services, the use of regular expressions is also used when judging URL resource links. The most mature and common practice in industry.
|
The most common way to match string content is to use regular expressions. Unfortunately, in traditional C++, regular expressions have not been supported by the language level, and are not included in the standard library. C++ is a high-performance language. In the development of background services, the use of regular expressions is also used when judging URL resource links. The most mature and common practice in the industry.
|
||||||
|
|
||||||
The general solution is to use the regular expression library of `boost`. C++11 officially incorporates the processing of regular expressions into the standard library, providing standard support from the language level and no longer relying on third parties.
|
The general solution is to use the regular expression library of `boost`. C++11 officially incorporates the processing of regular expressions into the standard library, providing standard support from the language level and no longer relying on third parties.
|
||||||
|
|
||||||
@@ -77,7 +75,7 @@ We use a simple example to briefly introduce the use of this library. Consider t
|
|||||||
|
|
||||||
- `[az]+\.txt`: In this regular expression, `[az]` means matching a lowercase letter, `+` can match the previous expression multiple times, so `[az]+` can Matches a string of lowercase letters. In the regular expression, a `.` means to match any character, and `\.` means to match the character `.`, and the last `txt` means to match `txt` exactly three letters. So the content of this regular expression to match is a text file consisting of pure lowercase letters.
|
- `[az]+\.txt`: In this regular expression, `[az]` means matching a lowercase letter, `+` can match the previous expression multiple times, so `[az]+` can Matches a string of lowercase letters. In the regular expression, a `.` means to match any character, and `\.` means to match the character `.`, and the last `txt` means to match `txt` exactly three letters. So the content of this regular expression to match is a text file consisting of pure lowercase letters.
|
||||||
|
|
||||||
`std::regex_match` is used to match strings and regular expressions, and there are many different overloaded forms. The simplest form is to pass `std::string` and a `std::regex` to match. When the match is successful, it will return `true`, otherwise it will return `false`. For example:
|
`std::regex_match` is used to match strings and regular expressions, and there are many different overloaded forms. The simplest form is to pass `std::string` and a `std::regex` to match. When the match is successful, it will return `true`, otherwise, it will return `false`. For example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -86,7 +84,9 @@ We use a simple example to briefly introduce the use of this library. Consider t
|
|||||||
|
|
||||||
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"};
|
||||||
// In C++, `\` will be used as an escape character in the string. In order for `\.` to be passed as a regular expression, it is necessary to perform second escaping of `\`, thus we have `\\.`
|
// In C++, `\` will be used as an escape character in the string.
|
||||||
|
// In order for `\.` to be passed as a regular expression,
|
||||||
|
// it is necessary to perform second escaping of `\`, thus we have `\\.`
|
||||||
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;
|
||||||
@@ -105,7 +105,8 @@ 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)) {
|
||||||
// the first element of std::smatch matches the entire string
|
// the first element of std::smatch matches the entire string
|
||||||
// the second element of std::smatch matches the first expression with brackets
|
// the second element of std::smatch matches the first expression
|
||||||
|
// with brackets
|
||||||
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;
|
||||||
@@ -131,14 +132,14 @@ bar.txt sub-match[1]: bar
|
|||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
This section briefly introduces the regular expression itself,
|
This section briefly introduces the regular expression itself,
|
||||||
and then introduces the use of the regular expression library
|
and then introduces the use of the regular expression library
|
||||||
through a practical example based on the main requirements of
|
through a practical example based on the main requirements of
|
||||||
using regular expressions.
|
using regular expressions.
|
||||||
|
|
||||||
## Exercise
|
## Exercise
|
||||||
|
|
||||||
In web server development, we usually want to serve some routes that satisfy a certain condition.
|
In web server development, we usually want to serve some routes that satisfy a certain condition.
|
||||||
Regular expressions are one of the tools to accomplish this.
|
Regular expressions are one of the tools to accomplish this.
|
||||||
Given the following request structure:
|
Given the following request structure:
|
||||||
|
|
||||||
@@ -188,16 +189,23 @@ Please implement the member functions `start()` and `parse_request`. Enable serv
|
|||||||
template<typename SERVER_TYPE>
|
template<typename SERVER_TYPE>
|
||||||
void start_server(SERVER_TYPE &server) {
|
void start_server(SERVER_TYPE &server) {
|
||||||
|
|
||||||
// process GET request for /match/[digit+numbers], e.g. GET request is /match/abc123, will return abc123
|
// process GET request for /match/[digit+numbers],
|
||||||
server.resource["fill_your_reg_ex"]["GET"] = [](ostream& response, Request& request) {
|
// e.g. GET request is /match/abc123, will return abc123
|
||||||
|
server.resource["fill_your_reg_ex"]["GET"] =
|
||||||
|
[](ostream& response, Request& request)
|
||||||
|
{
|
||||||
string number=request.path_match[1];
|
string number=request.path_match[1];
|
||||||
response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n" << number;
|
response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length()
|
||||||
|
<< "\r\n\r\n" << number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// peocess default GET request; anonymous function will be called if no other matches
|
// peocess default GET request;
|
||||||
// response files in folder web/
|
// anonymous function will be called
|
||||||
|
// if no other matches response files in folder web/
|
||||||
// default: index.html
|
// default: index.html
|
||||||
server.default_resource["fill_your_reg_ex"]["GET"] = [](ostream& response, Request& request) {
|
server.default_resource["fill_your_reg_ex"]["GET"] =
|
||||||
|
[](ostream& response, Request& request)
|
||||||
|
{
|
||||||
string filename = "www/";
|
string filename = "www/";
|
||||||
|
|
||||||
string path = request.path_match[1];
|
string path = request.path_match[1];
|
||||||
@@ -225,9 +233,9 @@ An suggested solution can be found [here](../../exercises/6).
|
|||||||
|
|
||||||
## Further Readings
|
## Further Readings
|
||||||
|
|
||||||
1. [Comments from `std::regex`'s author](http://zhihu.com/question/23070203/answer/84248248)
|
1. [Comments from `std::regex`'s author](https://zhihu.com/question/23070203/answer/84248248)
|
||||||
2. [Library document of Regular Expression](http://en.cppreference.com/w/cpp/regex)
|
2. [Library document of Regular Expression](https://en.cppreference.com/w/cpp/regex)
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -28,19 +28,20 @@ int main() {
|
|||||||
|
|
||||||
## 7.2 Mutex and Critical Section
|
## 7.2 Mutex and Critical Section
|
||||||
|
|
||||||
We have already learned the basics of concurrency technology in the operating system, or in the database, and `mutex` is one of the cores.
|
We have already learned the basics of concurrency technology in the operating system, or the database, and `mutex` is one of the cores.
|
||||||
C++11 introduces a class related to `mutex`, with all related functions in the `<mutex>` header file.
|
C++11 introduces a class related to `mutex`, with all related functions in the `<mutex>` header file.
|
||||||
|
|
||||||
`std::mutex` is the most basic `mutex` class in C++11, and you can create a mutex by instantiating `std::mutex`.
|
`std::mutex` is the most basic mutex class in C++11, and a mutex can be created by constructing a `std::mutex` object.
|
||||||
It can be locked by its member function `lock()`, and `unlock()` can be unlocked.
|
It can be locked by its member function `lock()`, and `unlock()` can be unlocked.
|
||||||
But in the process of actually writing the code, it is best not to directly call the member function,
|
But in the process of actually writing the code, it is best not to directly call the member function,
|
||||||
Because calling member functions, you need to call `unlock()` at the exit of each critical section, and of course, exceptions.
|
Because calling member functions, you need to call `unlock()` at the exit of each critical section, and of course, exceptions.
|
||||||
At this time, C++11 also provides a template class `std::lock_gurad` for the RAII syntax for the mutex.
|
At this time, C++11 also provides a template class `std::lock_guard` for the RAII mechanism for the mutex.
|
||||||
|
|
||||||
RAII guarantees the exceptional security of the code while losing the simplicity of the code.
|
RAII guarantees the exceptional security of the code while keeping the simplicity of the code.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
int v = 1;
|
int v = 1;
|
||||||
@@ -66,9 +67,11 @@ int main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
Because C++ guarantees that all stack objects will be destroyed at the end of the declaration period, such code is also extremely safe.
|
Because C++ guarantees that all stack objects will be destroyed at the end of the declaration period, such code is also extremely safe.
|
||||||
Whether `critical_section()` returns normally or if an exception is thrown in the middle, a stack rollback is thrown, and `unlock()` is automatically called.
|
Whether `critical_section()` returns normally or if an exception is thrown in the middle, a stack unwinding is thrown, and `unlock()` is automatically called.
|
||||||
|
|
||||||
And `std::unique_lock` is more flexible than `std::lock_guard`, `std::unique_lock` is more flexible.
|
> An exception is thrown and not caught (it is implementation-defined whether any stack unwinding is done in this case).
|
||||||
|
|
||||||
|
`std::unique_lock` is more flexible than `std::lock_guard`.
|
||||||
Objects of `std::unique_lock` manage the locking and unlocking operations on the `mutex` object with exclusive ownership (no other `unique_lock` objects owning the ownership of a `mutex` object). So in concurrent programming, it is recommended to use `std::unique_lock`.
|
Objects of `std::unique_lock` manage the locking and unlocking operations on the `mutex` object with exclusive ownership (no other `unique_lock` objects owning the ownership of a `mutex` object). So in concurrent programming, it is recommended to use `std::unique_lock`.
|
||||||
|
|
||||||
`std::lock_guard` cannot explicitly call `lock` and `unlock`, and `std::unique_lock` can be called anywhere after the declaration.
|
`std::lock_guard` cannot explicitly call `lock` and `unlock`, and `std::unique_lock` can be called anywhere after the declaration.
|
||||||
@@ -80,6 +83,7 @@ For instance:
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
int v = 1;
|
int v = 1;
|
||||||
@@ -114,10 +118,10 @@ int main() {
|
|||||||
## 7.3 Future
|
## 7.3 Future
|
||||||
|
|
||||||
The Future is represented by `std::future`, which provides a way to access the results of asynchronous operations. This sentence is very difficult to understand.
|
The Future is represented by `std::future`, which provides a way to access the results of asynchronous operations. This sentence is very difficult to understand.
|
||||||
In order to understand this feature, we need to understand the multi-threaded behavior before C++11.
|
To understand this feature, we need to understand the multi-threaded behavior before C++11.
|
||||||
|
|
||||||
Imagine if our main thread A wants to open a new thread B to perform some of our expected tasks and return me a result.
|
Imagine if our main thread A wants to open a new thread B to perform some of our expected tasks and return me a result.
|
||||||
At this time, thread A may be busy with other things, and have no time to take into account the results of B.
|
At this time, thread A may be busy with other things and have no time to take into account the results of B.
|
||||||
So we naturally hope to get the result of thread B at a certain time.
|
So we naturally hope to get the result of thread B at a certain time.
|
||||||
|
|
||||||
Before the introduction of `std::future` in C++11, the usual practice is:
|
Before the introduction of `std::future` in C++11, the usual practice is:
|
||||||
@@ -143,7 +147,8 @@ int main() {
|
|||||||
std::cout << "waiting...";
|
std::cout << "waiting...";
|
||||||
result.wait(); // block until future has arrived
|
result.wait(); // block until future has arrived
|
||||||
// output result
|
// output result
|
||||||
std::cout << "done!" << std:: endl << "future result is " << result.get() << std::endl;
|
std::cout << "done!" << std:: endl << "future result is "
|
||||||
|
<< result.get() << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -155,8 +160,8 @@ After encapsulating the target to be called, you can use `get_future()` to get a
|
|||||||
The condition variable `std::condition_variable` was born to solve the deadlock and was introduced when the mutex operation was not enough.
|
The condition variable `std::condition_variable` was born to solve the deadlock and was introduced when the mutex operation was not enough.
|
||||||
For example, a thread may need to wait for a condition to be true to continue execution.
|
For example, a thread may need to wait for a condition to be true to continue execution.
|
||||||
A dead wait loop can cause all other threads to fail to enter the critical section so that when the condition is true, a deadlock occurs.
|
A dead wait loop can cause all other threads to fail to enter the critical section so that when the condition is true, a deadlock occurs.
|
||||||
Therefore, the `condition_variable` instance is created primarily to wake up the waiting thread and avoid deadlocks.
|
Therefore, the `condition_variable` object is created primarily to wake up the waiting thread and avoid deadlocks.
|
||||||
`notd_one()` of `std::condition_variable` is used to wake up a thread;
|
`notify_one()` of `std::condition_variable` is used to wake up a thread;
|
||||||
`notify_all()` is to notify all threads. Below is an example of a producer and consumer model:
|
`notify_all()` is to notify all threads. Below is an example of a producer and consumer model:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -194,7 +199,8 @@ int main() {
|
|||||||
// temporal unlock to allow producer produces more rather than
|
// temporal unlock to allow producer produces more rather than
|
||||||
// let consumer hold the lock until its consumed.
|
// let consumer hold the lock until its consumed.
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // consumer is slower
|
// consumer is slower
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
lock.lock();
|
lock.lock();
|
||||||
if (!produced_nums.empty()) {
|
if (!produced_nums.empty()) {
|
||||||
std::cout << "consuming " << produced_nums.front() << std::endl;
|
std::cout << "consuming " << produced_nums.front() << std::endl;
|
||||||
@@ -217,15 +223,15 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It is worth mentioning that although we can use `notify_one()` in the producer, it is not really recommended to use it here.
|
It is worth mentioning that although we can use `notify_one()` in the producer, it is not recommended to use it here.
|
||||||
Because in the case of multiple consumers, our consumer implementation simply gives up the lock holding, which makes it possible for other consumers to compete for this lock, so as to better utilize the concurrency between multiple consumers. Having said that, but in fact because of the exclusivity of `std::mutex`,
|
Because in the case of multiple consumers, our consumer implementation simply gives up the lock holding, which makes it possible for other consumers to compete for this lock, to better utilize the concurrency between multiple consumers. Having said that, but in fact because of the exclusivity of `std::mutex`,
|
||||||
We simply can't expect multiple consumers to be able to actually produce content in a parallel consumer queue, and we still need a more granular approach.
|
We simply can't expect multiple consumers to be able to produce content in a parallel consumer queue, and we still need a more granular approach.
|
||||||
|
|
||||||
## 7.5 Atomic Operation and Memory Model
|
## 7.5 Atomic Operation and Memory Model
|
||||||
|
|
||||||
Careful readers may be tempted by the fact that the example of the producer consumer model in the previous section may have compiler optimizations that cause program errors.
|
Careful readers may be tempted by the fact that the example of the producer-consumer model in the previous section may have compiler optimizations that cause program errors.
|
||||||
For example, the boolean `notified` is not modified by `volatile`, and the compiler may have optimizations for this variable, such as the value of a register.
|
For example, the compiler may have optimizations for the variable `notified`, such as the value of a register.
|
||||||
As a result, the consumer thread can never observe the change of this value. This is a good question. To explain this problem, we need to further discuss the concept of memory model introduced from C++11. Let's first look at a question. What is the output of the following code?
|
As a result, the consumer thread can never observe the change of this value. This is a good question. To explain this problem, we need to further discuss the concept of the memory model introduced from C++11. Let's first look at a question. What is the output of the following code?
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -253,25 +259,25 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Intuitively, ʻa = 5;` in `t2` seems to always execute before `flag = 1;`, and `while (flag != 1)` in `t1` seems to guarantee `std ::cout << "b = " << b << std::endl;` will not be executed before the mark is changed. Logically, it seems that the value of `b` should be equal to 5.
|
Intuitively, it seems that `a = 5;` in `t2` always executes before `flag = 1;` and `while (flag != 1)` in `t1`. It looks like there is a guarantee the line `std ::cout << "b = " << b << std::endl;` will not be executed before the mark is changed. Logically, it seems that the value of `b` should be equal to 5.
|
||||||
But the actual situation is much more complicated than this, or the code itself is undefined behavior, because for `a` and `flag`, they are read and written in two parallel threads.
|
But the actual situation is much more complicated than this, or the code itself is undefined behavior because, for `a` and `flag`, they are read and written in two parallel threads.
|
||||||
There has been competition. In addition, even if we ignore competing reading and writing, it is still possible to receive out-of-order execution of the CPU, and the impact of the compiler on the rearrangement of instructions.
|
There has been competition. Also, even if we ignore competing for reading and writing, it is still possible to receive out-of-order execution of the CPU and the impact of the compiler on the rearrangement of instructions.
|
||||||
Cause `a = 5` to occur after `flag = 1`. Thus `b` may output 0.
|
Cause `a = 5` to occur after `flag = 1`. Thus `b` may output 0.
|
||||||
|
|
||||||
### Atomic Operation
|
### Atomic Operation
|
||||||
|
|
||||||
`std::mutex` can solve the problem of concurrent read and write, but the mutex is an operating system level function.
|
`std::mutex` can solve the problem of concurrent read and write, but the mutex is an operating system-level function.
|
||||||
This is because the implementation of a mutex usually contains two basic principles:
|
This is because the implementation of a mutex usually contains two basic principles:
|
||||||
|
|
||||||
1. Provide automatic state transition between threads, that is, "lock" state
|
1. Provide automatic state transition between threads, that is, "lock" state
|
||||||
2. Ensure that the memory of the manipulated variable is isolated from the critical section during the mutex operation
|
2. Ensure that the memory of the manipulated variable is isolated from the critical section during the mutex operation
|
||||||
|
|
||||||
This is a very strong set of synchronization conditions, in other words, when it is finally compiled into a CPU instruction, it will behave as a lot of instructions (we will look at how to implement a simple mutex later).
|
This is a very strong set of synchronization conditions, in other words when it is finally compiled into a CPU instruction, it will behave like a lot of instructions (we will look at how to implement a simple mutex later).
|
||||||
This seems too harsh for a variable that requires only atomic operations (no intermediate state).
|
This seems too harsh for a variable that requires only atomic operations (no intermediate state).
|
||||||
|
|
||||||
The research on synchronization conditions has a very long history, and we will not go into details here. Readers should understand that under the modern CPU architecture, atomic operations at the CPU instruction level are provided.
|
The research on synchronization conditions has a very long history, and we will not go into details here. Readers should understand that under the modern CPU architecture, atomic operations at the CPU instruction level are provided.
|
||||||
Therefore, in the C + + 11 multi-threaded shared variable reading and writing, the introduction of the `std::atomic` template, so that we instantiate an atomic type, will be a
|
Therefore, the `std::atomic` template is introduced in C++11 for the topic of multi-threaded shared variable reading and writing, which enables us to instantiate atomic types,
|
||||||
Atomic type read and write operations are minimized from a set of instructions to a single CPU instruction. E.g:
|
and minimize an atomic read or write operation from a set of instructions to a single CPU instruction. E.g:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::atomic<int> counter;
|
std::atomic<int> counter;
|
||||||
@@ -303,7 +309,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course, not all types provide atomic operations because the feasibility of atomic operations depends on the architecture of the CPU and whether the type structure being instantiated satisfies the memory alignment requirements of the architecture, so we can always pass Std::atomic<T>::is_lock_free` to check if the atom type needs to support atomic operations, for example:
|
Of course, not all types provide atomic operations because the feasibility of atomic operations depends on the architecture of the CPU and whether the type structure being instantiated satisfies the memory alignment requirements of the architecture, so we can always pass `std::atomic<T>::is_lock_free` to check if the atom type needs to support atomic operations, for example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -314,13 +320,15 @@ struct A {
|
|||||||
int y;
|
int y;
|
||||||
long long z;
|
long long z;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
std::atomic<A> a;
|
std::atomic<A> a;
|
||||||
std::cout << std::boolalpha << a.is_lock_free() << std::endl;
|
std::cout << std::boolalpha << a.is_lock_free() << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Concistency Model
|
### Consistency Model
|
||||||
|
|
||||||
Multiple threads executing in parallel, discussed at some macro level, can be roughly considered a distributed system.
|
Multiple threads executing in parallel, discussed at some macro level, can be roughly considered a distributed system.
|
||||||
In a distributed system, any communication or even local operation takes a certain amount of time, and even unreliable communication occurs.
|
In a distributed system, any communication or even local operation takes a certain amount of time, and even unreliable communication occurs.
|
||||||
@@ -333,206 +341,208 @@ Weakening the synchronization conditions between processes, usually we will cons
|
|||||||
|
|
||||||
1. Linear consistency: Also known as strong consistency or atomic consistency. It requires that any read operation can read the most recent write of a certain data, and the order of operation of all threads is consistent with the order under the global clock.
|
1. Linear consistency: Also known as strong consistency or atomic consistency. It requires that any read operation can read the most recent write of a certain data, and the order of operation of all threads is consistent with the order under the global clock.
|
||||||
|
|
||||||
```
|
```
|
||||||
x.store(1) x.load()
|
x.store(1) x.load()
|
||||||
T1 ---------+----------------+------>
|
T1 ---------+----------------+------>
|
||||||
|
|
||||||
|
|
||||||
T2 -------------------+------------->
|
T2 -------------------+------------->
|
||||||
x.store(2)
|
x.store(2)
|
||||||
```
|
```
|
||||||
|
|
||||||
In this case, thread `T1`, `T2` is twice atomic to `x`, and `x.store(1)` is strictly before `x.store(2)`. `x.store(2)` strictly occurs before `x.load()`. It is worth mentioning that linear consistency requirements for global clocks are difficult to achieve, which is why people continue to study other consistent algorithms under this weaker consistency.
|
In this case, thread `T1`, `T2` is twice atomic to `x`, and `x.store(1)` is strictly before `x.store(2)`. `x.store(2)` strictly occurs before `x.load()`. It is worth mentioning that linear consistency requirements for global clocks are difficult to achieve, which is why people continue to study other consistent algorithms under this weaker consistency.
|
||||||
|
|
||||||
2. Sequential consistency: It is also required that any read operation can read the last data written by the data, but it is not required to be consistent with the order of the global clock.
|
2. Sequential consistency: It is also required that any read operation can read the last data written by the data, but it is not required to be consistent with the order of the global clock.
|
||||||
|
|
||||||
```
|
```
|
||||||
x.store(1) x.store(3) x.load()
|
x.store(1) x.store(3) x.load()
|
||||||
T1 ---------+-----------+----------+----->
|
T1 ---------+-----------+----------+----->
|
||||||
|
|
||||||
|
|
||||||
T2 ---------------+---------------------->
|
T2 ---------------+---------------------->
|
||||||
x.store(2)
|
x.store(2)
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
x.store(1) x.store(3) x.load()
|
x.store(1) x.store(3) x.load()
|
||||||
T1 ---------+-----------+----------+----->
|
T1 ---------+-----------+----------+----->
|
||||||
|
|
||||||
|
|
||||||
T2 ------+------------------------------->
|
T2 ------+------------------------------->
|
||||||
x.store(2)
|
x.store(2)
|
||||||
```
|
```
|
||||||
|
|
||||||
Under the order consistency requirement, `x.load()` must read the last written data, so `x.store(2)` and `x.store(1)` do not have any guarantees, ie As long as ``x.store(2)` of `T2` occurs before `x.store(3)`.
|
Under the order consistency requirement, `x.load()` must read the last written data, so `x.store(2)` and `x.store(1)` do not have any guarantees, as long as `x.store(2)` of `T2` occurs before `x.store(3)`.
|
||||||
|
|
||||||
3. Causal consistency: its requirements are further reduced, only the sequence of causal operations is guaranteed, and the order of non-causal operations is not required.
|
3. Causal consistency: its requirements are further reduced, only the sequence of causal operations is guaranteed, and the order of non-causal operations is not required.
|
||||||
|
|
||||||
```
|
```
|
||||||
a = 1 b = 2
|
a = 1 b = 2
|
||||||
T1 ----+-----------+---------------------------->
|
T1 ----+-----------+---------------------------->
|
||||||
|
|
||||||
|
|
||||||
T2 ------+--------------------+--------+-------->
|
T2 ------+--------------------+--------+-------->
|
||||||
x.store(3) c = a + b y.load()
|
x.store(3) c = a + b y.load()
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
a = 1 b = 2
|
a = 1 b = 2
|
||||||
T1 ----+-----------+---------------------------->
|
T1 ----+-----------+---------------------------->
|
||||||
|
|
||||||
|
|
||||||
T2 ------+--------------------+--------+-------->
|
T2 ------+--------------------+--------+-------->
|
||||||
x.store(3) y.load() c = a + b
|
x.store(3) y.load() c = a + b
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
b = 2 a = 1
|
b = 2 a = 1
|
||||||
T1 ----+-----------+---------------------------->
|
T1 ----+-----------+---------------------------->
|
||||||
|
|
||||||
|
|
||||||
T2 ------+--------------------+--------+-------->
|
T2 ------+--------------------+--------+-------->
|
||||||
y.load() c = a + b x.store(3)
|
y.load() c = a + b x.store(3)
|
||||||
```
|
```
|
||||||
|
|
||||||
The three examples given above are all causal consistent, because in the whole process, only `c` has a dependency on `a` and `b`, and `x` and `y` are not related in this example. (But in actual situations we need more detailed information to determine that `x` is not related to `y`)
|
The three examples given above are all causal consistent because, in the whole process, only `c` has a dependency on `a` and `b`, and `x` and `y` are not related in this example. (But in actual situations we need more detailed information to determine that `x` is not related to `y`)
|
||||||
|
|
||||||
4. Final Consistency: It is the weakest consistency requirement. It only guarantees that an operation will be observed at a certain point in the future, but does not require the observed time. So we can even strengthen this condition a bit, for example, to specify that the time observed for an operation is always bounded. Of course this is no longer within our discussion.
|
4. Final Consistency: It is the weakest consistency requirement. It only guarantees that an operation will be observed at a certain point in the future, but does not require the observed time. So we can even strengthen this condition a bit, for example, to specify that the time observed for an operation is always bounded. Of course, this is no longer within our discussion.
|
||||||
|
|
||||||
```
|
```
|
||||||
x.store(3) x.store(4)
|
x.store(3) x.store(4)
|
||||||
T1 ----+-----------+-------------------------------------------->
|
T1 ----+-----------+-------------------------------------------->
|
||||||
|
|
||||||
|
|
||||||
T2 ---------+------------+--------------------+--------+-------->
|
T2 ---------+------------+--------------------+--------+-------->
|
||||||
x.read() x.read() x.read() x.read()
|
x.read() x.read() x.read() x.read()
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above case, if we assume that the initial value of x is 0, then the four times ``x.read()` in `T2` may be but not limited to the following:
|
In the above case, if we assume that the initial value of x is 0, then the four times ``x.read()` in `T2` may be but not limited to the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
3 4 4 4 // The write operation of x was quickly observed
|
3 4 4 4 // The write operation of x was quickly observed
|
||||||
0 3 3 4 // There is a delay in the observed time of the x write operation
|
0 3 3 4 // There is a delay in the observed time of the x write operation
|
||||||
0 0 0 4 // The last read read the final value of x, but the previous changes were not observed.
|
0 0 0 4 // The last read read the final value of x,
|
||||||
0 0 0 0 // The write operation of x is not observed in the current time period, but the situation that x is 4 can be observed at some point in the future.
|
// but the previous changes were not observed.
|
||||||
```
|
0 0 0 0 // The write operation of x is not observed in the current time period,
|
||||||
|
// but the situation that x is 4 can be observed
|
||||||
|
// at some point in the future.
|
||||||
|
```
|
||||||
|
|
||||||
### Memory Orders
|
### Memory Orders
|
||||||
|
|
||||||
In order to achieve the ultimate performance and achieve consistency of various strength requirements, C++11 defines six different memory sequences for atomic operations. The option `std::memory_order` expresses four synchronization models between multiple threads:
|
To achieve the ultimate performance and achieve consistency of various strength requirements, C++11 defines six different memory sequences for atomic operations. The option `std::memory_order` expresses four synchronization models between multiple threads:
|
||||||
|
|
||||||
1. Relaxed model: Under this model, atomic operations within a single thread are executed sequentially, and instruction reordering is not allowed, but the order of atomic operations between different threads is arbitrary. The type is specified by `std::memory_order_relaxed`. Let's look at an example:
|
1. Relaxed model: Under this model, atomic operations within a single thread are executed sequentially, and instruction reordering is not allowed, but the order of atomic operations between different threads is arbitrary. The type is specified by `std::memory_order_relaxed`. Let's look at an example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::atomic<int> counter = {0};
|
std::atomic<int> counter = {0};
|
||||||
std::vector<std::thread> vt;
|
std::vector<std::thread> vt;
|
||||||
for (int i = 0; i < 100; ++i) {
|
for (int i = 0; i < 100; ++i) {
|
||||||
vt.emplace_back([](){
|
vt.emplace_back([&](){
|
||||||
counter.fetch_add(1, std::memory_order_relaxed);
|
counter.fetch_add(1, std::memory_order_relaxed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& t : vt) {
|
for (auto& t : vt) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
std::cout << "current counter:" << counter << std::endl;
|
std::cout << "current counter:" << counter << std::endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
2. Release/consumption model: In this model, we begin to limit the order of operations between processes. If a thread needs to modify a value, but another thread will have a dependency on that operation of the value, that is, the latter depends on the former. Specifically, thread A has completed three writes to `x`, and thread `B` relies only on the third `x` write operation, regardless of the first two write behaviors of `x`, then `A ` When active `x.release()` (ie using `std::memory_order_release`), the option `std::memory_order_consume` ensures that `B` observes `A` when calling `x.load()` Three writes to `x`. Let's look at an example:
|
||||||
|
|
||||||
2. Release/consumption model: In this model, we begin to limit the order of operations between processes. If a thread needs to modify a value, but another thread will have a dependency on that operation of the value, that is, the latter depends. former. Specifically, thread A has completed three writes to `x`, and thread `B` relies only on the third `x` write operation, regardless of the first two write behaviors of `x`, then `A ` When active `x.release()` (ie using `std::memory_order_release`), the option `std::memory_order_consume` ensures that `B` observes `A` when calling `x.load()` Three writes to `x`. Let's look at an example:
|
```cpp
|
||||||
|
// initialize as nullptr to prevent consumer load a dangling pointer
|
||||||
|
std::atomic<int*> ptr(nullptr);
|
||||||
|
int v;
|
||||||
|
std::thread producer([&]() {
|
||||||
|
int* p = new int(42);
|
||||||
|
v = 1024;
|
||||||
|
ptr.store(p, std::memory_order_release);
|
||||||
|
});
|
||||||
|
std::thread consumer([&]() {
|
||||||
|
int* p;
|
||||||
|
while(!(p = ptr.load(std::memory_order_consume)));
|
||||||
|
|
||||||
```cpp
|
std::cout << "p: " << *p << std::endl;
|
||||||
std::atomic<int*> ptr;
|
std::cout << "v: " << v << std::endl;
|
||||||
int v;
|
});
|
||||||
std::thread producer([&]() {
|
producer.join();
|
||||||
int* p = new int(42);
|
consumer.join();
|
||||||
v = 1024;
|
```
|
||||||
ptr.store(p, std::memory_order_release);
|
|
||||||
});
|
|
||||||
std::thread consumer([&]() {
|
|
||||||
int* p;
|
|
||||||
while(!(p = ptr.load(std::memory_order_consume)));
|
|
||||||
|
|
||||||
std::cout << "p: " << *p << std::endl;
|
3. Release/Acquire model: Under this model, we can further tighten the order of atomic operations between different threads, specifying the timing between releasing `std::memory_order_release` and getting `std::memory_order_acquire`. **All** write operations before the release operation is visible to any other thread, i.e., happens before.
|
||||||
std::cout << "v: " << v << std::endl;
|
|
||||||
});
|
|
||||||
producer.join();
|
|
||||||
consumer.join();
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Release/Acquire model: Under this model, we can further tighten the order of atomic operations between different threads, specifying the timing between releasing `std::memory_order_release` and getting `std::memory_order_acquire`. **All** write operations before the release operation are visible to any other thread, ie, happens-before.
|
As you can see, `std::memory_order_release` ensures that a write before a release does not occur after the release operation, which is a **backward barrier**, and `std::memory_order_acquire` ensures that a subsequent read or write after a acquire does not occur before the acquire operation, which is a **forward barrier**.
|
||||||
|
For the `std::memory_order_acq_rel` option, combines the characteristics of the two barriers and determines a unique memory barrier, such that reads and writes of the current thread will not be rearranged across the barrier.
|
||||||
|
|
||||||
As you can see, `std::memory_order_release` ensures that the write behavior after it does not occur before the release operation, is a backward barrier, and `std::memory_order_acquire` ensures the previous write behavior after it, no It will happen after the get operation, it is a forward barrier. For the option `std::memory_order_acq_rel`, it combines the characteristics of the two, and only determines a memory barrier, so that the current thread reads and writes to the memory. Will not be rearranged before and after this operation.
|
Let's check an example:
|
||||||
|
|
||||||
Let's check an example:
|
```cpp
|
||||||
|
std::vector<int> v;
|
||||||
|
std::atomic<int> flag = {0};
|
||||||
|
std::thread release([&]() {
|
||||||
|
v.push_back(42);
|
||||||
|
flag.store(1, std::memory_order_release);
|
||||||
|
});
|
||||||
|
std::thread acqrel([&]() {
|
||||||
|
int expected = 1; // must before compare_exchange_strong
|
||||||
|
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel))
|
||||||
|
expected = 1; // must after compare_exchange_strong
|
||||||
|
// flag has changed to 2
|
||||||
|
});
|
||||||
|
std::thread acquire([&]() {
|
||||||
|
while(flag.load(std::memory_order_acquire) < 2);
|
||||||
|
|
||||||
```cpp
|
std::cout << v.at(0) << std::endl; // must be 42
|
||||||
std::vector<int> v;
|
});
|
||||||
std::atomic<int> flag = {0};
|
release.join();
|
||||||
std::thread release([&]() {
|
acqrel.join();
|
||||||
v.push_back(42);
|
acquire.join();
|
||||||
flag.store(1, std::memory_order_release);
|
```
|
||||||
});
|
|
||||||
std::thread acqrel([&]() {
|
|
||||||
int expected = 1; // must before compare_exchange_strong
|
|
||||||
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) {
|
|
||||||
expected = 1; // must after compare_exchange_strong
|
|
||||||
}
|
|
||||||
// flag has changed to 2
|
|
||||||
});
|
|
||||||
std::thread acquire([&]() {
|
|
||||||
while(flag.load(std::memory_order_acquire) < 2);
|
|
||||||
|
|
||||||
std::cout << v.at(0) << std::endl; // must be 42
|
In this case we used `compare_exchange_strong`, which is the Compare-and-swap primitive, which has a weaker version, `compare_exchange_weak`, which allows a failure to be returned even if the exchange is successful. The reason is due to a false failure on some platforms, specifically when the CPU performs a context switch, another thread loads the same address to produce an inconsistency. In addition, the performance of `compare_exchange_strong` may be slightly worse than `compare_exchange_weak`. However, in most cases, `compare_exchange_weak` is discouraged due to the complexity of its usage.
|
||||||
});
|
|
||||||
release.join();
|
|
||||||
acqrel.join();
|
|
||||||
acquire.join();
|
|
||||||
```
|
|
||||||
|
|
||||||
In this case we used `compare_exchange_strong`, which is the Compare-and-swap primitive, which has a weaker version, `compare_exchange_weak`, which allows a failure to be returned even if the exchange is successful. The reason is due to a false failure on some platforms, specifically, when the CPU performs a context switch, another thread loads the same address to produce an inconsistency. In addition, the performance of `compare_exchange_strong` may be slightly worse than `compare_exchange_weak`, but in most cases, `compare_exchange_strong` should be limited.
|
4. Sequential Consistent Model: Under this model, atomic operations satisfy sequence consistency, which in turn can cause performance loss. It can be specified explicitly by `std::memory_order_seq_cst`. Let's look at a final example:
|
||||||
|
|
||||||
4. Sequential Consistent Model: Under this model, atomic operations satisfy sequence consistency, which in turn can cause performance loss. It can be specified explicitly by `std::memory_order_seq_cst`. Let's look a final example:
|
```cpp
|
||||||
|
std::atomic<int> counter = {0};
|
||||||
|
std::vector<std::thread> vt;
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
vt.emplace_back([&](){
|
||||||
|
counter.fetch_add(1, std::memory_order_seq_cst);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
```cpp
|
for (auto& t : vt) {
|
||||||
std::atomic<int> counter = {0};
|
t.join();
|
||||||
std::vector<std::thread> vt;
|
}
|
||||||
for (int i = 0; i < 100; ++i) {
|
std::cout << "current counter:" << counter << std::endl;
|
||||||
vt.emplace_back([](){
|
```
|
||||||
counter.fetch_add(1, std::memory_order_seq_cst);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& t : vt) {
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
std::cout << "current counter:" << counter << std::endl;
|
|
||||||
```
|
|
||||||
|
|
||||||
This example is essentially the same as the first loose model example. Just change the memory order of the atomic operation to `memory_order_seq_cst`. Interested readers can write their own programs to measure the performance difference caused by these two different memory sequences.
|
|
||||||
|
|
||||||
|
This example is essentially the same as the first loose model example. Just change the memory order of the atomic operation to `memory_order_seq_cst`. Interested readers can write their own programs to measure the performance difference caused by these two different memory sequences.
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
The C++11 language layer provides support for concurrent programming. This section briefly introduces `std::thread`/`std::mutex`/`std::future`, an important tool that can't be avoided in concurrent programming.
|
The C++11 language layer provides support for concurrent programming. This section briefly introduces `std::thread`/`std::mutex`/`std::future`, an important tool that can't be avoided in concurrent programming.
|
||||||
In addition, we also introduced the "memory model" as one of the most important features of C++11.
|
In addition, we also introduced the "memory model" as one of the most important features of C++11.
|
||||||
They provide an critical foundation for standardized high performance computing for C++.
|
They provide a critical foundation for standardized high-performance computing for C++.
|
||||||
|
|
||||||
## Exercises
|
## Exercises
|
||||||
|
|
||||||
1. Write a simple thread pool that provides the following features:
|
1. Write a simple thread pool that provides the following features:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
ThreadPool p(4); // specify four work thread
|
ThreadPool p(4); // specify four work thread
|
||||||
|
|
||||||
// enqueue a task, and return a std::future
|
// enqueue a task, and return a std::future
|
||||||
auto f = pool.enqueue([](int life) {
|
auto f = pool.enqueue([](int life) {
|
||||||
return meaning;
|
return meaning;
|
||||||
}, 42);
|
}, 42);
|
||||||
|
|
||||||
// fetch result from future
|
// fetch result from future
|
||||||
std::cout << f.get() << std::endl;
|
std::cout << f.get() << std::endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Use `std::atomic<bool>` to implement a mutex.
|
2. Use `std::atomic<bool>` to implement a mutex.
|
||||||
|
|
||||||
@@ -540,10 +550,10 @@ They provide an critical foundation for standardized high performance computing
|
|||||||
|
|
||||||
## Further Readings
|
## Further Readings
|
||||||
|
|
||||||
- [C++ 并发编程(中文版)](https://www.amazon.com/dp/1617294691/ref=cm_sw_em_r_mt_dp_U_siEmDbRMMF960)
|
- [C++ Concurrency in Action](https://www.amazon.com/dp/1617294691/ref=cm_sw_em_r_mt_dp_U_siEmDbRMMF960)
|
||||||
- [Thread document](http://en.cppreference.com/w/cpp/thread)
|
- [Thread document](https://en.cppreference.com/w/cpp/thread)
|
||||||
- Herlihy, M. P., & Wing, J. M. (1990). Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems, 12(3), 463–492. https://doi.org/10.1145/78969.78972
|
- Herlihy, M. P., & Wing, J. M. (1990). Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems, 12(3), 463–492. https://doi.org/10.1145/78969.78972
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).`
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).`
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ order: 8
|
|||||||
|
|
||||||
The file system library provides functions related to
|
The file system library provides functions related to
|
||||||
the operation of the file system, path, regular files, directories, and so on.
|
the operation of the file system, path, regular files, directories, and so on.
|
||||||
Similar to the regular expression library, he was one of the first libraries
|
Similar to the regular expression library, it was one of the first libraries
|
||||||
to be launched by boost and eventually merged into the C++ standard.
|
to be launched by boost and eventually merged into the C++ standard.
|
||||||
|
|
||||||
## 8.1 Document and Link
|
## 8.1 Document and Link
|
||||||
@@ -27,4 +27,4 @@ TODO:
|
|||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
@@ -12,22 +12,20 @@ order: 9
|
|||||||
|
|
||||||
### `long long int`
|
### `long long int`
|
||||||
|
|
||||||
`long long int` is not the first to be introduced in C++11.
|
`long long int` is not the first to be introduced in C++11.
|
||||||
In fact, as early as C99, `long long int` has been included in the C standard,
|
As early as C99, `long long int` has been included in the C standard,
|
||||||
so most compilers already support it.
|
so most compilers already support it.
|
||||||
C++11 now formally incorporate it into the standard library,
|
C++11 now formally incorporate it into the standard library,
|
||||||
specifying a `long long int` type with at least 64 bits.
|
specifying a `long long int` type with at least 64 bits.
|
||||||
|
|
||||||
## 9.2 `noexcept` and Its Operations
|
## 9.2 `noexcept` and Its Operations
|
||||||
|
|
||||||
One of the big advantages of C++ over C is that
|
One of the big advantages of C++ over C is that
|
||||||
C++ itself defines a complete set of exception handling mechanisms.
|
C++ itself defines a complete set of exception handling mechanisms.
|
||||||
However, before C++11, almost no one used to write
|
However, before C++11, almost no one used to write an exception declaration expression after the function name.
|
||||||
an exception declaration expression after the function name.
|
Starting from C++11, this mechanism was deprecated,
|
||||||
Starting from C++11, this mechanism was deprecated,
|
so we will not discuss or introduce the previous mechanism.
|
||||||
so we will not discuss or introduce the previous mechanism.
|
How to work and how to use it, you should not take the initiative to understand it.
|
||||||
How to work and how to use it, you should not take the initiative
|
|
||||||
to understand it.
|
|
||||||
|
|
||||||
C++11 simplifies exception declarations into two cases:
|
C++11 simplifies exception declarations into two cases:
|
||||||
|
|
||||||
@@ -41,13 +39,13 @@ void may_throw(); // May throw any exception
|
|||||||
void no_throw() noexcept; // Cannot throw any exception
|
void no_throw() noexcept; // Cannot throw any exception
|
||||||
```
|
```
|
||||||
|
|
||||||
If a function modified with `noexcept` is thrown,
|
If a function modified with `noexcept` is thrown,
|
||||||
the compiler will use `std::terminate()` to
|
the compiler will use `std::terminate()` to
|
||||||
immediately terminate the program.
|
immediately terminate the program.
|
||||||
|
|
||||||
`noexcept` can also be used as an operator to manipulate an expression.
|
`noexcept` can also be used as an operator to manipulate an expression.
|
||||||
When the expression has no exception, it returns `true`,
|
When the expression has no exception, it returns `true`,
|
||||||
otherwise it returns `false`.
|
otherwise, it returns `false`.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -75,15 +73,15 @@ int main()
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`noexcept` can modify the function of blocking exceptions
|
`noexcept` can modify the function of blocking exceptions
|
||||||
after modifying a function. If an exception is generated internally,
|
after modifying a function. If an exception is generated internally,
|
||||||
the external will not trigger. For instance:
|
the external will not trigger. For instance:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
try {
|
try {
|
||||||
may_throw();
|
may_throw();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
std::cout << "exception captured from my_throw()" << std::endl;
|
std::cout << "exception captured from may_throw()" << std::endl;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
non_block_throw();
|
non_block_throw();
|
||||||
@@ -100,7 +98,7 @@ try {
|
|||||||
The final output is:
|
The final output is:
|
||||||
|
|
||||||
```
|
```
|
||||||
exception captured, from my_throw()
|
exception captured, from may_throw()
|
||||||
exception captured, from non_block_throw()
|
exception captured, from non_block_throw()
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -108,13 +106,13 @@ exception captured, from non_block_throw()
|
|||||||
|
|
||||||
### Raw String Literal
|
### Raw String Literal
|
||||||
|
|
||||||
In traditional C++, it is very painful to write a string full of
|
In traditional C++, it is very painful to write a string full of
|
||||||
special characters. For example, a string containing HTML ontology
|
special characters. For example, a string containing HTML ontology
|
||||||
needs to add a large number of escape characters.
|
needs to add a large number of escape characters.
|
||||||
For example, a file path on Windows often as: `C:\\Path\\To\\File`.
|
For example, a file path on Windows often as: `C:\\Path\\To\\File`.
|
||||||
|
|
||||||
C++11 provides the original string literals,
|
C++11 provides the original string literals,
|
||||||
which can be decorated with `R` in front of a string,
|
which can be decorated with `R` in front of a string,
|
||||||
and the original string is wrapped in parentheses, for example:
|
and the original string is wrapped in parentheses, for example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -130,7 +128,7 @@ int main() {
|
|||||||
|
|
||||||
### Custom Literal
|
### Custom Literal
|
||||||
|
|
||||||
C++11 introduces the ability to customize literals by
|
C++11 introduces the ability to customize literals by
|
||||||
overloading the double quotes suffix operator:
|
overloading the double quotes suffix operator:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -190,19 +188,19 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
where `std::max_align_t` requires exactly the same alignment for each scalar type, so it has almost no difference in maximum scalars.
|
where `std::max_align_t` requires the same alignment for each scalar type, so it has almost no difference in maximum scalars.
|
||||||
In turn, the result on most platforms is `long double`, so the alignment requirement for `AlignasStorage` we get here is 8 or 16.
|
In turn, the result on most platforms is `long double`, so the alignment requirement for `AlignasStorage` we get here is 8 or 16.
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
Several of the features introduced in this section are those that
|
Several of the features introduced in this section are those that
|
||||||
use more frequent features from modern C++ features that
|
use more frequent features from modern C++ features that
|
||||||
have not yet been introduced. `noexcept` is the most important feature.
|
have not yet been introduced. `noexcept` is the most important feature.
|
||||||
One of its features is to prevent the spread of anomalies,
|
One of its features is to prevent the spread of anomalies,
|
||||||
effective Let the compiler optimize our code to the maximum extent possible.
|
effective Let the compiler optimize our code to the maximum extent possible.
|
||||||
|
|
||||||
[Table of Content](./toc.md) | [Previous Chapter](./08-filesystem.md) | [Next Chapter: Outlook: Introduction of C++20](./10-cpp20.md)
|
[Table of Content](./toc.md) | [Previous Chapter](./08-filesystem.md) | [Next Chapter: Outlook: Introduction of C++20](./10-cpp20.md)
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -9,22 +9,22 @@ order: 10
|
|||||||
[TOC]
|
[TOC]
|
||||||
|
|
||||||
C++20 seems to be an exciting update.
|
C++20 seems to be an exciting update.
|
||||||
For example, as early as C++11, the `Concept`,
|
For example, as early as C++11, the `Concept`,
|
||||||
which was eager to call for high-altitude but ultimately lost, is now on the line.
|
which was eager to call for high-altitude but ultimately lost, is now on the line.
|
||||||
The C++ Organizing Committee decided to vote to finalize C++20 with many proposals,
|
The C++ Organizing Committee decided to vote to finalize C++20 with many proposals,
|
||||||
such as **Concepts**/**Module**/**Coroutine**/**Ranges**/ and so on.
|
such as **Concepts**/**Module**/**Coroutine**/**Ranges**/ and so on.
|
||||||
In this chapter we'll take a look at some of the important features that
|
In this chapter, we'll take a look at some of the important features that
|
||||||
C++20 will introduce.
|
C++20 will introduce.
|
||||||
|
|
||||||
## Concept
|
## Concept
|
||||||
|
|
||||||
Concept is a further enhancement to C++ template programming.
|
The concept is a further enhancement to C++ template programming.
|
||||||
In simple terms, the concept is a compile-time feature.
|
In simple terms, the concept is a compile-time feature.
|
||||||
It allows the compiler to evaluate template parameters at compile time,
|
It allows the compiler to evaluate template parameters at compile-time,
|
||||||
greatly enhancing our experience with template programming in C++.
|
greatly enhancing our experience with template programming in C++.
|
||||||
When programming with templates, we often encounter a variety of heinous errors.
|
When programming with templates, we often encounter a variety of heinous errors.
|
||||||
This is because we have so far been unable to check and limit template parameters.
|
This is because we have so far been unable to check and limit template parameters.
|
||||||
For example, the following two lines of code can cause a lot of
|
For example, the following two lines of code can cause a lot of
|
||||||
almost unreadable compilation errors:
|
almost unreadable compilation errors:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -37,12 +37,12 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The root cause of this code error is that `std::sort` must provide
|
The root cause of this code error is that `std::sort` must provide
|
||||||
a random iterator for the sorting container, otherwise it will not be used,
|
a random iterator for the sorting container, otherwise it will not be used,
|
||||||
and we know that `std::list` does not support random access.
|
and we know that `std::list` does not support random access.
|
||||||
In the conceptual language, the iterator in `std::list` does not satisfy
|
In the conceptual language, the iterator in `std::list` does not satisfy
|
||||||
the constraint of the concept of random iterators in `std::sort`.
|
the constraint of the concept of random iterators in `std::sort`.
|
||||||
After introducing the concept, we can constrain the template parameters
|
After introducing the concept, we can constrain the template parameters
|
||||||
like this:
|
like this:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -94,9 +94,9 @@ This is still full of charm for a programming language that is already in its th
|
|||||||
## Further Readings
|
## Further Readings
|
||||||
|
|
||||||
- [Why Concepts didn't make C++17?](http://honermann.net/blog/2016/03/06/why-concepts-didnt-make-cxx17/)
|
- [Why Concepts didn't make C++17?](http://honermann.net/blog/2016/03/06/why-concepts-didnt-make-cxx17/)
|
||||||
- [C++11/14/17/20 Compiler Support](http://en.cppreference.com/w/cpp/compiler_support)
|
- [C++11/14/17/20 Compiler Support](https://en.cppreference.com/w/cpp/compiler_support)
|
||||||
- [C++ History](https://en.cppreference.com/w/cpp/language/history)
|
- [C++ History](https://en.cppreference.com/w/cpp/language/history)
|
||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ First of all, congratulations 🎉 on reading this book! I hope this book has ra
|
|||||||
|
|
||||||
As mentioned in the introduction to this book, this book is just a book that takes you quickly to the new features of modern C++ 11/14/17/20, rather than the advanced learning practice of C++ "Black Magic". The author of course also thinks about this demand, but the content is very difficult and there are few audiences. Here, the author lists some materials that can help you learn more about modern C++ based on this book. I hope I can help you:
|
As mentioned in the introduction to this book, this book is just a book that takes you quickly to the new features of modern C++ 11/14/17/20, rather than the advanced learning practice of C++ "Black Magic". The author of course also thinks about this demand, but the content is very difficult and there are few audiences. Here, the author lists some materials that can help you learn more about modern C++ based on this book. I hope I can help you:
|
||||||
|
|
||||||
- [C++ Reference](http://en.cppreference.com/w)
|
- [C++ Reference](https://en.cppreference.com/w)
|
||||||
- [CppCon YouTube Channel](https://www.youtube.com/user/CppCon/videos)
|
- [CppCon YouTube Channel](https://www.youtube.com/user/CppCon/videos)
|
||||||
- [Ulrich Drepper. What Every Programmer Should Know About Memory. 2007](https://people.freebsd.org/~lstewart/articles/cpumemory.pdf)
|
- [Ulrich Drepper. What Every Programmer Should Know About Memory. 2007](https://people.freebsd.org/~lstewart/articles/cpumemory.pdf)
|
||||||
- to be added
|
- to be added
|
||||||
@@ -19,4 +19,4 @@ As mentioned in the introduction to this book, this book is just a book that tak
|
|||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
@@ -36,4 +36,4 @@ TODO:
|
|||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
@@ -109,4 +109,4 @@ Table of Content | Last Chapter | [Next Chapter: Preface](./00-preface.md)
|
|||||||
|
|
||||||
## Licenses
|
## Licenses
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work was written by [Ou Changkun](https://changkun.de) and licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>. The code of this repository is open sourced under the [MIT license](../../LICENSE).
|
||||||
@@ -11,11 +11,11 @@ order: 0
|
|||||||
## 引言
|
## 引言
|
||||||
|
|
||||||
C++ 是一个用户群体相当大的语言。从 C++98 的出现到 C++11 的正式定稿经历了长达十年多之久的积累。C++14/17 则是作为对 C++11 的重要补充和优化,C++20 则将这门语言领进了现代化的大门,所有这些新标准中扩充的特性,给 C++ 这门语言注入了新的活力。
|
C++ 是一个用户群体相当大的语言。从 C++98 的出现到 C++11 的正式定稿经历了长达十年多之久的积累。C++14/17 则是作为对 C++11 的重要补充和优化,C++20 则将这门语言领进了现代化的大门,所有这些新标准中扩充的特性,给 C++ 这门语言注入了新的活力。
|
||||||
那些还在坚持使用**传统 C++**(本书把 C++98 及其之前的 C++ 特性均称之为传统 C++)而未接触过现代 C++ 的 C++ 程序员在见到诸如 Lambda 表达式这类全新特性时,甚至会流露出『学的不是同一门语言』的惊叹之情。
|
那些还在坚持使用**传统 C++** (本书把 C++98 及其之前的 C++ 特性均称之为传统 C++)而未接触过现代 C++ 的 C++ 程序员在见到诸如 Lambda 表达式这类全新特性时,甚至会流露出『学的不是同一门语言』的惊叹之情。
|
||||||
|
|
||||||
**现代 C++** (本书中均指 C++11/14/17/20) 为传统 C++ 注入的大量特性使得整个 C++ 变得更加像一门现代化的语言。现代 C++ 不仅仅增强了 C++ 语言自身的可用性,`auto` 关键字语义的修改使得我们更加有信心来操控极度复杂的模板类型。同时还对语言运行期进行了大量的强化,Lambda 表达式的出现让 C++ 具有了『匿名函数』的『闭包』特性,而这一特性几乎在现代的编程语言(诸如 Python/Swift/... )中已经司空见惯,右值引用的出现解决了 C++ 长期以来被人诟病的临时对象效率问题等等。
|
**现代 C++** (本书中均指 C++11/14/17/20) 为传统 C++ 注入的大量特性使得整个 C++ 变得更加像一门现代化的语言。现代 C++ 不仅仅增强了 C++ 语言自身的可用性,`auto` 关键字语义的修改使得我们更加有信心来操控极度复杂的模板类型。同时还对语言运行期进行了大量的强化,Lambda 表达式的出现让 C++ 具有了『匿名函数』的『闭包』特性,而这一特性几乎在现代的编程语言(诸如 Python/Swift/... )中已经司空见惯,右值引用的出现解决了 C++ 长期以来被人诟病的临时对象效率问题等等。
|
||||||
|
|
||||||
C++17 则是近三年依赖 C++ 社区一致推进的方向,也指出了**现代C++**编程的一个重要发展方向。尽管它的出现并不如 C++11 的分量之重,但它包含了大量小而美的语言与特性(例如结构化绑定),这些特性的出现再一次修正了我们在 C++ 中的编程范式。
|
C++17 则是近三年依赖 C++ 社区一致推进的方向,也指出了 **现代C++** 编程的一个重要发展方向。尽管它的出现并不如 C++11 的分量之重,但它包含了大量小而美的语言与特性(例如结构化绑定),这些特性的出现再一次修正了我们在 C++ 中的编程范式。
|
||||||
|
|
||||||
现代 C++ 还为自身的标准库增加了非常多的工具和方法,诸如在语言自身标准的层面上制定了 `std::thread`,从而支持了并发编程,在不同平台上不再依赖于系统底层的 API,实现了语言层面的跨平台支持;`std::regex` 提供了完整的正则表达式支持等等。C++98 已经被实践证明了是一种非常成功的『范型』,而现代 C++ 的出现,则进一步推动这种范型,让 C++ 成为系统程序设计和库开发更好的语言。Concept 提供了对模板参数编译期的检查,进一步增强了语言整体的可用性。
|
现代 C++ 还为自身的标准库增加了非常多的工具和方法,诸如在语言自身标准的层面上制定了 `std::thread`,从而支持了并发编程,在不同平台上不再依赖于系统底层的 API,实现了语言层面的跨平台支持;`std::regex` 提供了完整的正则表达式支持等等。C++98 已经被实践证明了是一种非常成功的『范型』,而现代 C++ 的出现,则进一步推动这种范型,让 C++ 成为系统程序设计和库开发更好的语言。Concept 提供了对模板参数编译期的检查,进一步增强了语言整体的可用性。
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ C++17 则是近三年依赖 C++ 社区一致推进的方向,也指出了**现
|
|||||||
|
|
||||||
## 目标读者
|
## 目标读者
|
||||||
|
|
||||||
1. 本书假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++进行编码的人、渴望在短时间内迅速了解**现代 C++** 特性的人非常适合阅读本书;
|
1. 本书假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++ 进行编码的人、渴望在短时间内迅速了解**现代 C++** 特性的人非常适合阅读本书;
|
||||||
2. 本书一定程度上介绍了一些现代 C++ 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习现代 C++ 的读者,本书的定位系**现代 C++ 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 **现代 C++** 的熟悉度。
|
2. 本书一定程度上介绍了一些现代 C++ 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习现代 C++ 的读者,本书的定位系**现代 C++ 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 **现代 C++** 的熟悉度。
|
||||||
|
|
||||||
## 本书目的
|
## 本书目的
|
||||||
@@ -46,6 +46,6 @@ C++17 则是近三年依赖 C++ 社区一致推进的方向,也指出了**现
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ InstalledDir: /Library/Developer/CommandLineTools/usr/bin
|
|||||||
> **注意**:弃用并非彻底不能用,只是用于暗示程序员这些特性将从未来的标准中消失,应该尽量避免使用。但是,已弃用的特性依然是标准库的一部分,并且出于兼容性的考虑,大部分特性其实会『永久』保留。
|
> **注意**:弃用并非彻底不能用,只是用于暗示程序员这些特性将从未来的标准中消失,应该尽量避免使用。但是,已弃用的特性依然是标准库的一部分,并且出于兼容性的考虑,大部分特性其实会『永久』保留。
|
||||||
|
|
||||||
- **不再允许字符串字面值常量赋值给一个 `char *`。如果需要用字符串字面值常量赋值和初始化一个 `char *`,应该使用 `const char *` 或者 `auto`。**
|
- **不再允许字符串字面值常量赋值给一个 `char *`。如果需要用字符串字面值常量赋值和初始化一个 `char *`,应该使用 `const char *` 或者 `auto`。**
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
char *str = "hello world!"; // 将出现弃用警告
|
char *str = "hello world!"; // 将出现弃用警告
|
||||||
```
|
```
|
||||||
@@ -91,7 +92,7 @@ int main() {
|
|||||||
gcc -c foo.c
|
gcc -c foo.c
|
||||||
```
|
```
|
||||||
|
|
||||||
编译出 `foo.o` 文件,再使用 `clang++` 将 C++代码和 `.o` 文件链接起来(或者都编译为 `.o` 再统一链接):
|
编译出 `foo.o` 文件,再使用 `clang++` 将 C++ 代码和 `.o` 文件链接起来(或者都编译为 `.o` 再统一链接):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
clang++ 1.1.cpp foo.o -std=c++2a -o 1.1
|
clang++ 1.1.cpp foo.o -std=c++2a -o 1.1
|
||||||
@@ -120,7 +121,7 @@ clean:
|
|||||||
|
|
||||||
> 注意:`Makefile` 中的缩进是制表符而不是空格符,如果你直接复制这段代码到你的编辑器中,制表符可能会被自动替换掉,请自行确保在 `Makefile` 中的缩进是由制表符完成的。
|
> 注意:`Makefile` 中的缩进是制表符而不是空格符,如果你直接复制这段代码到你的编辑器中,制表符可能会被自动替换掉,请自行确保在 `Makefile` 中的缩进是由制表符完成的。
|
||||||
>
|
>
|
||||||
> 如果你还不知道 Makefile 的使用也没有关系,本教程中不会构建过于复杂的代码,简单的在命令行中使用 `clang++ -std=c++2a` 也可以阅读本书。
|
> 如果你还不知道 `Makefile` 的使用也没有关系,本教程中不会构建过于复杂的代码,简单的在命令行中使用 `clang++ -std=c++2a` 也可以阅读本书。
|
||||||
|
|
||||||
如果你是首次接触现代 C++,那么你很可能还看不懂上面的那一小段代码,即:
|
如果你是首次接触现代 C++,那么你很可能还看不懂上面的那一小段代码,即:
|
||||||
|
|
||||||
@@ -137,12 +138,12 @@ clean:
|
|||||||
## 进一步阅读的参考文献
|
## 进一步阅读的参考文献
|
||||||
|
|
||||||
- [C++ 语言导学. Bjarne Stroustrup](https://www.amazon.cn/dp/B00WUBYBYS/ref=sr_1_1?ie=UTF8&qid=1522400738&sr=8-1&keywords=C%2B%2B+%E8%AF%AD%E8%A8%80%E5%AF%BC%E5%AD%A6)
|
- [C++ 语言导学. Bjarne Stroustrup](https://www.amazon.cn/dp/B00WUBYBYS/ref=sr_1_1?ie=UTF8&qid=1522400738&sr=8-1&keywords=C%2B%2B+%E8%AF%AD%E8%A8%80%E5%AF%BC%E5%AD%A6)
|
||||||
- [C++ 历史](http://en.cppreference.com/w/cpp/language/history)
|
- [C++ 历史](https://en.cppreference.com/w/cpp/language/history)
|
||||||
- [C++ 特性在 GCC/Clang 等编译器中的支持情况](http://en.cppreference.com/w/cpp/compiler_support)
|
- [C++ 特性在 GCC/Clang 等编译器中的支持情况](https://en.cppreference.com/w/cpp/compiler_support)
|
||||||
- [C++98 与 C99 之间的区别](http://david.tribble.com/text/cdiffs.htm#C99-vs-CPP98)
|
- [C++98 与 C99 之间的区别](http://david.tribble.com/text/cdiffs.htm#C99-vs-CPP98)
|
||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -14,15 +14,15 @@ order: 2
|
|||||||
|
|
||||||
### nullptr
|
### nullptr
|
||||||
|
|
||||||
`nullptr` 出现的目的是为了替代 `NULL`。在某种意义上来说,传统 C++ 会把 `NULL`、`0` 视为同一种东西,这取决于编译器如何定义 NULL,有些编译器会将 NULL 定义为 `((void*)0)`,有些则会直接将其定义为 `0`。
|
`nullptr` 出现的目的是为了替代 `NULL`。 C 与 C++ 语言中有**空指针常量**,它们能被隐式转换成任何指针类型的空指针值,或 C++ 中的任何成员指针类型的空成员指针值。 `NULL` 由标准库实现提供,并被定义为实现定义的空指针常量。在 C 中,有些标准库会把 `NULL` 定义为 `((void*)0)` 而有些将它定义为 `0`。
|
||||||
|
|
||||||
C++ **不允许**直接将 `void *` 隐式转换到其他类型。但如果编译器尝试把 `NULL` 定义为 `((void*)0)`,那么在下面这句代码中:
|
C++ **不允许**直接将 `void *` 隐式转换到其他类型,从而 `((void*)0)` 不是 `NULL` 的合法实现。如果标准库尝试把 `NULL` 定义为 `((void*)0)`,那么下面这句代码中会出现编译错误:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
char *ch = NULL;
|
char *ch = NULL;
|
||||||
```
|
```
|
||||||
|
|
||||||
没有了 `void *` 隐式转换的 C++ 只好将`NULL` 定义为 `0`。而这依然会产生新的问题,将 `NULL` 定义成 0 将导致 `C++` 中重载特性发生混乱。考虑下面这两个 `foo` 函数:
|
没有了 `void *` 隐式转换的 C++ 只好将 `NULL` 定义为 `0`。而这依然会产生新的问题,将 `NULL` 定义成 `0` 将导致 `C++` 中重载特性发生混乱。考虑下面这两个 `foo` 函数:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void foo(char*);
|
void foo(char*);
|
||||||
@@ -31,9 +31,9 @@ void foo(int);
|
|||||||
|
|
||||||
那么 `foo(NULL);` 这个语句将会去调用 `foo(int)`,从而导致代码违反直觉。
|
那么 `foo(NULL);` 这个语句将会去调用 `foo(int)`,从而导致代码违反直觉。
|
||||||
|
|
||||||
为了解决这个问题,C++11 引入了 `nullptr` 关键字,专门用来区分空指针、0。而 `nullptr` 的类型为 `nullptr_t`,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。
|
为了解决这个问题,C++11 引入了 `nullptr` 关键字,专门用来区分空指针、`0`。而 `nullptr` 的类型为 `nullptr_t`,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。
|
||||||
|
|
||||||
你可以尝试使用 clang++ 编译下面的代码:
|
你可以尝试使用 `clang++` 编译下面的代码:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -73,11 +73,11 @@ foo(char*) is called
|
|||||||
|
|
||||||
从输出中我们可以看出,`NULL` 不同于 `0` 与 `nullptr`。所以,请养成直接使用 `nullptr`的习惯。
|
从输出中我们可以看出,`NULL` 不同于 `0` 与 `nullptr`。所以,请养成直接使用 `nullptr`的习惯。
|
||||||
|
|
||||||
此外,在上面的代码中,我们使用了 `decltype` 和 `std::is_same` 这两个属于现代 C++ 的语法,简单来说,`decltype` 用于类型推导,而 `std::is_same` 用于比较两个类型是否相等,我们会在后面 [decltype](#decltype) 一节中详细讨论。
|
此外,在上面的代码中,我们使用了 `decltype` 和 `std::is_same` 这两个属于现代 C++ 的语法,简单来说,`decltype` 用于类型推导,而 `std::is_same` 用于比较两个类型是否相同,我们会在后面 [decltype](#decltype) 一节中详细讨论。
|
||||||
|
|
||||||
### constexpr
|
### constexpr
|
||||||
|
|
||||||
C++ 本身已经具备了常量表达式的概念,比如 1+2, 3*4 这种表达式总是会产生相同的结果并且没有任何副作用。如果编译器能够在编译时就把这些表达式直接优化并植入到程序运行时,将能增加程序的性能。一个非常明显的例子就是在数组的定义阶段:
|
C++ 本身已经具备了常量表达式的概念,比如 `1+2`, `3*4` 这种表达式总是会产生相同的结果并且没有任何副作用。如果编译器能够在编译时就把这些表达式直接优化并植入到程序运行时,将能增加程序的性能。一个非常明显的例子就是在数组的定义阶段:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -123,7 +123,7 @@ int main() {
|
|||||||
|
|
||||||
C++11 提供了 `constexpr` 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式,这个关键字明确的告诉编译器应该去验证 `len_foo` 在编译期就应该是一个常量表达式。
|
C++11 提供了 `constexpr` 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式,这个关键字明确的告诉编译器应该去验证 `len_foo` 在编译期就应该是一个常量表达式。
|
||||||
|
|
||||||
此外,`constexpr` 的函数可以使用递归:
|
此外,`constexpr` 修饰的函数可以使用递归:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
constexpr int fibonacci(const int n) {
|
constexpr int fibonacci(const int n) {
|
||||||
@@ -131,7 +131,7 @@ constexpr int fibonacci(const int n) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
从 C++14 开始,constexpr 函数可以在内部使用局部变量、循环和分支等简单语句,例如下面的代码在 C++11 的标准下是不能够通过编译的:
|
从 C++14 开始,`constexpr` 函数可以在内部使用局部变量、循环和分支等简单语句,例如下面的代码在 C++11 的标准下是不能够通过编译的:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
constexpr int fibonacci(const int n) {
|
constexpr int fibonacci(const int n) {
|
||||||
@@ -153,7 +153,7 @@ constexpr int fibonacci(const int n) {
|
|||||||
|
|
||||||
### if/switch 变量声明强化
|
### if/switch 变量声明强化
|
||||||
|
|
||||||
在传统 C++ 中,变量的声明在虽然能够位于任何位置,甚至于 `for` 语句内能够声明一个临时变量 `int`,但始终没有办法在 `if` 和 `switch` 语句中声明一个临时的变量。例如:
|
在传统 C++ 中,变量的声明虽然能够位于任何位置,甚至于 `for` 语句内能够声明一个临时变量 `int`,但始终没有办法在 `if` 和 `switch` 语句中声明一个临时的变量。例如:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -176,12 +176,13 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 将输出 1, 4, 3, 4
|
// 将输出 1, 4, 3, 4
|
||||||
for (std::vector<int>::iterator element = vec.begin(); element != vec.end(); ++element)
|
for (std::vector<int>::iterator element = vec.begin(); element != vec.end();
|
||||||
|
++element)
|
||||||
std::cout << *element << std::endl;
|
std::cout << *element << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
在上面的代码中,我们可以看到 `itr` 这一变量是定义在整个 `main()` 的作用域内的,这导致当我们需要再次遍历整个 `std::vectors` 时,需要重新命名另一个变量。C++17 消除了这一限制,使得我们可以在 if(或 switch)中完成这一操作:
|
在上面的代码中,我们可以看到 `itr` 这一变量是定义在整个 `main()` 的作用域内的,这导致当我们需要再次遍历整个 `std::vector` 时,需要重新命名另一个变量。C++17 消除了这一限制,使得我们可以在 `if`(或 `switch`)中完成这一操作:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// 将临时变量放到 if 语句内
|
// 将临时变量放到 if 语句内
|
||||||
@@ -228,15 +229,18 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
为了解决这个问题,C++11 首先把初始化列表的概念绑定到了类型上,并将其称之为 `std::initializer_list`,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:
|
为解决这个问题,C++11 首先把初始化列表的概念绑定到类型上,称其为 `std::initializer_list`,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
class MagicFoo {
|
class MagicFoo {
|
||||||
public:
|
public:
|
||||||
std::vector<int> vec;
|
std::vector<int> vec;
|
||||||
MagicFoo(std::initializer_list<int> list) {
|
MagicFoo(std::initializer_list<int> list) {
|
||||||
for (std::initializer_list<int>::iterator it = list.begin();
|
for (std::initializer_list<int>::iterator it = list.begin();
|
||||||
it != list.end(); ++it)
|
it != list.end(); ++it)
|
||||||
vec.push_back(*it);
|
vec.push_back(*it);
|
||||||
}
|
}
|
||||||
@@ -246,7 +250,9 @@ int main() {
|
|||||||
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
||||||
|
|
||||||
std::cout << "magicFoo: ";
|
std::cout << "magicFoo: ";
|
||||||
for (std::vector<int>::iterator it = magicFoo.vec.begin(); it != magicFoo.vec.end(); ++it) std::cout << *it << std::endl;
|
for (std::vector<int>::iterator it = magicFoo.vec.begin();
|
||||||
|
it != magicFoo.vec.end(); ++it)
|
||||||
|
std::cout << *it << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -255,9 +261,10 @@ int main() {
|
|||||||
初始化列表除了用在对象构造上,还能将其作为普通函数的形参,例如:
|
初始化列表除了用在对象构造上,还能将其作为普通函数的形参,例如:
|
||||||
|
|
||||||
```Cpp
|
```Cpp
|
||||||
public:
|
public:
|
||||||
void foo(std::initializer_list<int> list) {
|
void foo(std::initializer_list<int> list) {
|
||||||
for (std::initializer_list<int>::iterator it = list.begin(); it != list.end(); ++it) vec.push_back(*it);
|
for (std::initializer_list<int>::iterator it = list.begin();
|
||||||
|
it != list.end(); ++it) vec.push_back(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
magicFoo.foo({6,7,8,9});
|
magicFoo.foo({6,7,8,9});
|
||||||
@@ -277,6 +284,7 @@ C++17 完善了这一设定,给出的结构化绑定可以让我们写出这
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
std::tuple<int, double, std::string> f() {
|
std::tuple<int, double, std::string> f() {
|
||||||
return std::make_tuple(1, 2.3, "456");
|
return std::make_tuple(1, 2.3, "456");
|
||||||
@@ -293,7 +301,7 @@ int main() {
|
|||||||
|
|
||||||
## 2.3 类型推导
|
## 2.3 类型推导
|
||||||
|
|
||||||
在传统 C 和 C++中,参数的类型都必须明确定义,这其实对我们快速进行编码没有任何帮助,尤其是当我们面对一大堆复杂的模板类型时,必须明确的指出变量的类型才能进行后续的编码,这不仅拖慢我们的开发效率,也让代码变得又臭又长。
|
在传统 C 和 C++ 中,参数的类型都必须明确定义,这其实对我们快速进行编码没有任何帮助,尤其是当我们面对一大堆复杂的模板类型时,必须明确的指出变量的类型才能进行后续的编码,这不仅拖慢我们的开发效率,也让代码变得又臭又长。
|
||||||
|
|
||||||
C++11 引入了 `auto` 和 `decltype` 这两个关键字实现了类型推导,让编译器来操心变量的类型。这使得 C++ 也具有了和其他现代编程语言一样,某种意义上提供了无需操心变量类型的使用习惯。
|
C++11 引入了 `auto` 和 `decltype` 这两个关键字实现了类型推导,让编译器来操心变量的类型。这使得 C++ 也具有了和其他现代编程语言一样,某种意义上提供了无需操心变量类型的使用习惯。
|
||||||
|
|
||||||
@@ -306,8 +314,8 @@ C++11 引入了 `auto` 和 `decltype` 这两个关键字实现了类型推导,
|
|||||||
```cpp
|
```cpp
|
||||||
// 在 C++11 之前
|
// 在 C++11 之前
|
||||||
// 由于 cbegin() 将返回 vector<int>::const_iterator
|
// 由于 cbegin() 将返回 vector<int>::const_iterator
|
||||||
// 所以 itr 也应该是 vector<int>::const_iterator 类型
|
// 所以 it 也应该是 vector<int>::const_iterator 类型
|
||||||
for(vector<int>::const_iterator it = vec.cbegin(); itr != vec.cend(); ++it)
|
for(vector<int>::const_iterator it = vec.cbegin(); it != vec.cend(); ++it)
|
||||||
```
|
```
|
||||||
|
|
||||||
而有了 `auto` 之后可以:
|
而有了 `auto` 之后可以:
|
||||||
@@ -346,28 +354,37 @@ auto i = 5; // i 被推导为 int
|
|||||||
auto arr = new auto(10); // arr 被推导为 int *
|
auto arr = new auto(10); // arr 被推导为 int *
|
||||||
```
|
```
|
||||||
|
|
||||||
> **注意**:`auto` 不能用于函数传参,因此下面的做法是无法通过编译的(考虑重载的问题,我们应该使用模板):
|
从 C++ 14 起,`auto` 能用于 lambda 表达式中的函数传参,而 C++ 20 起该功能推广到了一般的函数。考虑下面的例子:
|
||||||
|
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto add14 = [](auto x, auto y) -> int {
|
||||||
|
return x+y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add20(auto x, auto y) {
|
||||||
|
return x+y;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto i = 5; // type int
|
||||||
|
auto j = 6; // type int
|
||||||
|
std::cout << add14(i, j) << std::endl;
|
||||||
|
std::cout << add20(i, j) << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
>
|
||||||
|
> **注意**:`auto` 还不能用于推导数组类型:
|
||||||
>
|
>
|
||||||
> ```cpp
|
> ```cpp
|
||||||
> int add(auto x, auto y);
|
> auto auto_arr2[10] = {arr}; // 错误, 无法推导数组元素类型
|
||||||
>
|
|
||||||
> 2.6.auto.cpp:16:9: error: 'auto' not allowed in function prototype
|
|
||||||
> int add(auto x, auto y) {
|
|
||||||
> ^~~~
|
|
||||||
> ```
|
|
||||||
>
|
|
||||||
> 此外,`auto` 还不能用于推导数组类型:
|
|
||||||
>
|
|
||||||
> ```cpp
|
|
||||||
> auto auto_arr2[10] = arr; // 错误, 无法推导数组元素类型
|
|
||||||
>
|
>
|
||||||
> 2.6.auto.cpp:30:19: error: 'auto_arr2' declared as array of 'auto'
|
> 2.6.auto.cpp:30:19: error: 'auto_arr2' declared as array of 'auto'
|
||||||
> auto auto_arr2[10] = arr;
|
> auto auto_arr2[10] = {arr};
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
### decltype
|
### decltype
|
||||||
|
|
||||||
`decltype` 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 `sizeof` 很相似:
|
`decltype` 关键字是为了解决 `auto` 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 `typeof` 很相似:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
decltype(表达式)
|
decltype(表达式)
|
||||||
@@ -401,19 +418,19 @@ type z == type x
|
|||||||
|
|
||||||
### 尾返回类型推导
|
### 尾返回类型推导
|
||||||
|
|
||||||
你可能会思考,在介绍 `auto`时,我们已经提过 `auto` 不能用于函数形参进行类型推导,那么 `auto` 能不能用于推导函数的返回类型呢?还是考虑一个加法函数的例子,在传统 C++ 中我们必须这么写:
|
你可能会思考, `auto` 能不能用于推导函数的返回类型呢?还是考虑一个加法函数的例子,在传统 C++ 中我们必须这么写:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<typename R, typename T, typename U>
|
template<typename R, typename T, typename U>
|
||||||
R add(T x, U y) {
|
R add(T x, U y) {
|
||||||
return x+y
|
return x+y;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> 注意:typename 和 class 在模板参数列表中没有区别,在 typename 这个关键字出现之前,都是使用 class 来定义模板参数的。但在模板中定义有[嵌套依赖类型](http://en.cppreference.com/w/cpp/language/dependent_name#The_typename_disambiguator_for_dependent_names)的变量时,需要用 typename 消除歧义
|
> 注意:typename 和 class 在模板参数列表中没有区别,在 typename 这个关键字出现之前,都是使用 class 来定义模板参数的。但在模板中定义有[嵌套依赖类型](https://en.cppreference.com/w/cpp/language/dependent_name#The_typename_disambiguator_for_dependent_names)的变量时,需要用 typename 消除歧义
|
||||||
|
|
||||||
|
|
||||||
这样的代码其实变得很丑陋,因为程序员在使用这个模板函数的时候,必须明确指出返回类型。但事实上我们并不知道 `add()` 这个函数会做什么样的操作,获得一个什么样的返回类型。
|
这样的代码其实变得很丑陋,因为程序员在使用这个模板函数的时候,必须明确指出返回类型。但事实上我们并不知道 `add()` 这个函数会做什么样的操作,以及获得一个什么样的返回类型。
|
||||||
|
|
||||||
在 C++11 中这个问题得到解决。虽然你可能马上会反应出来使用 `decltype` 推导 `x+y` 的类型,写出这样的代码:
|
在 C++11 中这个问题得到解决。虽然你可能马上会反应出来使用 `decltype` 推导 `x+y` 的类型,写出这样的代码:
|
||||||
|
|
||||||
@@ -421,7 +438,7 @@ R add(T x, U y) {
|
|||||||
decltype(x+y) add(T x, U y)
|
decltype(x+y) add(T x, U y)
|
||||||
```
|
```
|
||||||
|
|
||||||
但事实上这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,`x` 和 `y` 尚未被定义。为了解决这个问题,C++11 还引入了一个叫做尾返回类型(trailing return type),利用 auto 关键字将返回类型后置:
|
但事实上这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,`x` 和 `y` 尚未被定义。为了解决这个问题,C++11 还引入了一个叫做尾返回类型(trailing return type),利用 `auto` 关键字将返回类型后置:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
@@ -573,7 +590,7 @@ extern template class std::vector<double>; // 不在该当前编译文件中实
|
|||||||
std::vector<std::vector<int>> matrix;
|
std::vector<std::vector<int>> matrix;
|
||||||
```
|
```
|
||||||
|
|
||||||
这在传统C++编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于下下面这种写法都能够通过编译:
|
这在传统 C++ 编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于像下面这种写法都能够通过编译:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<bool T>
|
template<bool T>
|
||||||
@@ -617,28 +634,6 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 默认模板参数
|
|
||||||
|
|
||||||
我们可能定义了一个加法函数:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
template<typename T, typename U>
|
|
||||||
auto add(T x, U y) -> decltype(x+y) {
|
|
||||||
return x+y;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
但在使用时发现,要使用 add,就必须每次都指定其模板参数的类型。
|
|
||||||
|
|
||||||
在 C++11 中提供了一种便利,可以指定模板的默认参数:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
template<typename T = int, typename U = int>
|
|
||||||
auto add(T x, U y) -> decltype(x+y) {
|
|
||||||
return x+y;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 变长参数模板
|
### 变长参数模板
|
||||||
|
|
||||||
模板一直是 C++ 所独有的**黑魔法**(一起念:**Dark Magic**)之一。
|
模板一直是 C++ 所独有的**黑魔法**(一起念:**Dark Magic**)之一。
|
||||||
@@ -659,9 +654,9 @@ class Magic<int,
|
|||||||
std::vector<int>>> darkMagic;
|
std::vector<int>>> darkMagic;
|
||||||
```
|
```
|
||||||
|
|
||||||
既然是任意形式,所以个数为 0 的模板参数也是可以的:`class Magic<> nothing;`。
|
既然是任意形式,所以个数为 `0` 的模板参数也是可以的:`class Magic<> nothing;`。
|
||||||
|
|
||||||
如果不希望产生的模板参数个数为0,可以手动的定义至少一个模板参数:
|
如果不希望产生的模板参数个数为 `0`,可以手动的定义至少一个模板参数:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<typename Require, typename... Args> class Magic;
|
template<typename Require, typename... Args> class Magic;
|
||||||
@@ -670,7 +665,7 @@ template<typename Require, typename... Args> class Magic;
|
|||||||
变长参数模板也能被直接调整到到模板函数上。传统 C 中的 `printf` 函数,
|
变长参数模板也能被直接调整到到模板函数上。传统 C 中的 `printf` 函数,
|
||||||
虽然也能达成不定个数的形参的调用,但其并非类别安全。
|
虽然也能达成不定个数的形参的调用,但其并非类别安全。
|
||||||
而 C++11 除了能定义类别安全的变长参数函数外,
|
而 C++11 除了能定义类别安全的变长参数函数外,
|
||||||
还可以使类似 printf 的函数能自然地处理非自带类别的对象。
|
还可以使类似 `printf` 的函数能自然地处理非自带类别的对象。
|
||||||
除了在模板参数中能使用 `...` 表示不定长模板参数外,
|
除了在模板参数中能使用 `...` 表示不定长模板参数外,
|
||||||
函数参数也使用同样的表示法代表不定长参数,
|
函数参数也使用同样的表示法代表不定长参数,
|
||||||
这也就为我们简单编写变长参数函数提供了便捷的手段,例如:
|
这也就为我们简单编写变长参数函数提供了便捷的手段,例如:
|
||||||
@@ -777,7 +772,7 @@ int main() {
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
auto add(T t, U u) {
|
auto add(T t, U u) {
|
||||||
return t+u;
|
return t+u;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -823,6 +818,7 @@ int main() {
|
|||||||
C++11 引入了委托构造的概念,这使得构造函数可以在同一个类中一个构造函数调用另一个构造函数,从而达到简化代码的目的:
|
C++11 引入了委托构造的概念,这使得构造函数可以在同一个类中一个构造函数调用另一个构造函数,从而达到简化代码的目的:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
class Base {
|
class Base {
|
||||||
public:
|
public:
|
||||||
int value1;
|
int value1;
|
||||||
@@ -844,9 +840,10 @@ int main() {
|
|||||||
|
|
||||||
### 继承构造
|
### 继承构造
|
||||||
|
|
||||||
在传统 C++ 中,构造函数如果需要继承是需要将参数一一传递的,这将导致效率低下。C++11 利用关键字 using 引入了继承构造函数的概念:
|
在传统 C++ 中,构造函数如果需要继承是需要将参数一一传递的,这将导致效率低下。C++11 利用关键字 `using` 引入了继承构造函数的概念:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
class Base {
|
class Base {
|
||||||
public:
|
public:
|
||||||
int value1;
|
int value1;
|
||||||
@@ -871,7 +868,7 @@ int main() {
|
|||||||
|
|
||||||
### 显式虚函数重载
|
### 显式虚函数重载
|
||||||
|
|
||||||
在传统 C++中,经常容易发生意外重载虚函数的事情。例如:
|
在传统 C++ 中,经常容易发生意外重载虚函数的事情。例如:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct Base {
|
struct Base {
|
||||||
@@ -888,7 +885,7 @@ C++11 引入了 `override` 和 `final` 这两个关键字来防止上述情形
|
|||||||
|
|
||||||
#### override
|
#### override
|
||||||
|
|
||||||
当重载虚函数时,引入 `override` 关键字将显式的告知编译器进行重载,编译器将检查基函数是否存在这样的虚函数,否则将无法通过编译:
|
当重载虚函数时,引入 `override` 关键字将显式的告知编译器进行重载,编译器将检查基函数是否存在这样的其函数签名一致的虚函数,否则将无法通过编译:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct Base {
|
struct Base {
|
||||||
@@ -966,19 +963,21 @@ enum class new_enum : unsigned int {
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
if (new_enum::value3 == new_enum::value4) {
|
if (new_enum::value3 == new_enum::value4) {
|
||||||
// 会输出
|
// 会输出true
|
||||||
std::cout << "new_enum::value3 == new_enum::value4" << std::endl;
|
std::cout << "new_enum::value3 == new_enum::value4" << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
在这个语法中,枚举类型后面使用了冒号及类型关键字来指定枚举中枚举值的类型,这使得我们能够为枚举赋值(未指定时将默认使用 int)。
|
在这个语法中,枚举类型后面使用了冒号及类型关键字来指定枚举中枚举值的类型,这使得我们能够为枚举赋值(未指定时将默认使用 `int`)。
|
||||||
|
|
||||||
而我们希望获得枚举值的值时,将必须显式的进行类型转换,不过我们可以通过重载 `<<` 这个算符来进行输出,可以收藏下面这个代码段:
|
而我们希望获得枚举值的值时,将必须显式的进行类型转换,不过我们可以通过重载 `<<` 这个算符来进行输出,可以收藏下面这个代码段:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
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);
|
||||||
}
|
}
|
||||||
@@ -994,8 +993,8 @@ std::cout << new_enum::value3 << std::endl
|
|||||||
|
|
||||||
本节介绍了现代 C++ 中对语言可用性的增强,其中笔者认为最为重要的几个特性是几乎所有人都需要了解并熟练使用的:
|
本节介绍了现代 C++ 中对语言可用性的增强,其中笔者认为最为重要的几个特性是几乎所有人都需要了解并熟练使用的:
|
||||||
|
|
||||||
1. auto 类型推导
|
1. `auto` 类型推导
|
||||||
2. 范围 for 迭代
|
2. 范围 `for` 迭代
|
||||||
3. 初始化列表
|
3. 初始化列表
|
||||||
4. 变参模板
|
4. 变参模板
|
||||||
|
|
||||||
@@ -1030,6 +1029,6 @@ std::cout << new_enum::value3 << std::endl
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ Lambda 表达式的基本语法如下:
|
|||||||
上面的语法规则除了 `[捕获列表]` 内的东西外,其他部分都很好理解,只是一般函数的函数名被略去,
|
上面的语法规则除了 `[捕获列表]` 内的东西外,其他部分都很好理解,只是一般函数的函数名被略去,
|
||||||
返回值使用了一个 `->` 的形式进行(我们在上一节前面的尾返回类型已经提到过这种写法了)。
|
返回值使用了一个 `->` 的形式进行(我们在上一节前面的尾返回类型已经提到过这种写法了)。
|
||||||
|
|
||||||
所谓捕获列表,其实可以理解为参数的一种类型,lambda 表达式内部函数体在默认情况下是不能够使用函数体外部的变量的,
|
所谓捕获列表,其实可以理解为参数的一种类型,Lambda 表达式内部函数体在默认情况下是不能够使用函数体外部的变量的,
|
||||||
这时候捕获列表可以起到传递外部数据的作用。根据传递的行为,捕获列表也分为以下几种:
|
这时候捕获列表可以起到传递外部数据的作用。根据传递的行为,捕获列表也分为以下几种:
|
||||||
|
|
||||||
#### 1. 值捕获
|
#### 1. 值捕获
|
||||||
|
|
||||||
与参数传值类似,值捕获的前提是变量可以拷贝,不同之处则在于,被捕获的变量在 lambda 表达式被创建时拷贝,
|
与参数传值类似,值捕获的前提是变量可以拷贝,不同之处则在于,被捕获的变量在 Lambda 表达式被创建时拷贝,
|
||||||
而非调用时才拷贝:
|
而非调用时才拷贝:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -67,19 +67,19 @@ void lambda_reference_capture() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**3. 隐式捕获**
|
#### 3. 隐式捕获
|
||||||
|
|
||||||
手动书写捕获列表有时候是非常复杂的,这种机械性的工作可以交给编译器来处理,这时候可以在捕获列表中写一个
|
手动书写捕获列表有时候是非常复杂的,这种机械性的工作可以交给编译器来处理,这时候可以在捕获列表中写一个
|
||||||
`&` 或 `=` 向编译器声明采用引用捕获或者值捕获.
|
`&` 或 `=` 向编译器声明采用引用捕获或者值捕获.
|
||||||
|
|
||||||
总结一下,捕获提供了lambda 表达式对外部值进行使用的功能,捕获列表的最常用的四种形式可以是:
|
总结一下,捕获提供了 Lambda 表达式对外部值进行使用的功能,捕获列表的最常用的四种形式可以是:
|
||||||
|
|
||||||
- \[\] 空捕获列表
|
- \[\] 空捕获列表
|
||||||
- \[name1, name2, ...\] 捕获一系列变量
|
- \[name1, name2, ...\] 捕获一系列变量
|
||||||
- \[&\] 引用捕获, 让编译器自行推导捕获列表
|
- \[&\] 引用捕获, 从函数体内的使用确定引用捕获列表
|
||||||
- \[=\] 值捕获, 让编译器执行推导应用列表
|
- \[=\] 值捕获, 从函数体内的使用确定值捕获列表
|
||||||
|
|
||||||
**4. 表达式捕获**
|
#### 4. 表达式捕获
|
||||||
|
|
||||||
> 这部分内容需要了解后面马上要提到的右值引用以及智能指针
|
> 这部分内容需要了解后面马上要提到的右值引用以及智能指针
|
||||||
|
|
||||||
@@ -90,29 +90,26 @@ C++14 给与了我们方便,允许捕获的成员用任意的表达式进行
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <utility>
|
#include <memory> // std::make_unique
|
||||||
|
#include <utility> // std::move
|
||||||
|
|
||||||
int main() {
|
void lambda_expression_capture() {
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
在上面的代码中,`important` 是一个独占指针,是不能够被捕获到的,这时候我们需要将其转移为右值,
|
在上面的代码中,important 是一个独占指针,是不能够被 "=" 值捕获到,这时候我们可以将其转移为右值,在表达式中初始化。
|
||||||
在表达式中初始化。
|
|
||||||
|
|
||||||
### 泛型 Lambda
|
### 泛型 Lambda
|
||||||
|
|
||||||
上一节中我们提到了 `auto` 关键字不能够用在参数表里,这是因为这样的写法会与模板的功能产生冲突。
|
上一节中我们提到了 `auto` 关键字不能够用在参数表里,这是因为这样的写法会与模板的功能产生冲突。
|
||||||
但是 Lambda 表达式并不是普通函数,所以 Lambda 表达式并不能够模板化。
|
但是 Lambda 表达式并不是普通函数,所以在没有明确指明参数表类型的情况下,Lambda 表达式并不能够模板化。
|
||||||
这就为我们造成了一定程度上的麻烦:参数表不能够泛化,必须明确参数表类型。
|
幸运的是,这种麻烦只存在于 C++11 中,从 C++14 开始,Lambda 函数的形式参数可以使用 `auto`
|
||||||
|
关键字来产生意义上的泛型:
|
||||||
幸运的是,这种麻烦只存在于 C++11 中,从 C++14 开始,
|
|
||||||
Lambda 函数的形式参数可以使用 `auto` 关键字来产生意义上的泛型:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
auto add = [](auto x, auto y) {
|
auto add = [](auto x, auto y) {
|
||||||
@@ -137,7 +134,7 @@ Lambda 表达式的本质是一个和函数对象类型相似的类类型(称
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using foo = void(int); // 定义函数类型, using 的使用见上一节中的别名语法
|
using foo = void(int); // 定义函数类型, using 的使用见上一节中的别名语法
|
||||||
void functional(foo f) { // 定义在参数列表中的函数类型 foo 被视为退化后的函数指针类型 foo*
|
void functional(foo f) { // 参数列表中定义的函数类型 foo 被视为退化后的函数指针类型 foo*
|
||||||
f(1); // 通过函数指针调用函数
|
f(1); // 通过函数指针调用函数
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +154,7 @@ int main() {
|
|||||||
|
|
||||||
C++11 `std::function` 是一种通用、多态的函数封装,
|
C++11 `std::function` 是一种通用、多态的函数封装,
|
||||||
它的实例可以对任何可以调用的目标实体进行存储、复制和调用操作,
|
它的实例可以对任何可以调用的目标实体进行存储、复制和调用操作,
|
||||||
它也是对 C++中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的),
|
它也是对 C++ 中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的),
|
||||||
换句话说,就是函数的容器。当我们有了函数的容器之后便能够更加方便的将函数、函数指针作为对象进行处理。
|
换句话说,就是函数的容器。当我们有了函数的容器之后便能够更加方便的将函数、函数指针作为对象进行处理。
|
||||||
例如:
|
例如:
|
||||||
|
|
||||||
@@ -194,7 +191,8 @@ 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);
|
||||||
@@ -214,18 +212,44 @@ int main() {
|
|||||||
|
|
||||||
要弄明白右值引用到底是怎么一回事,必须要对左值和右值做一个明确的理解。
|
要弄明白右值引用到底是怎么一回事,必须要对左值和右值做一个明确的理解。
|
||||||
|
|
||||||
**左值(lvalue, left value)**,顾名思义就是赋值符号左边的值。准确来说,
|
**左值** (lvalue, left value),顾名思义就是赋值符号左边的值。准确来说,
|
||||||
左值是表达式(不一定是赋值表达式)后依然存在的持久对象。
|
左值是表达式(不一定是赋值表达式)后依然存在的持久对象。
|
||||||
|
|
||||||
**右值(rvalue, right value)**,右边的值,是指表达式结束后就不再存在的临时对象。
|
**右值** (rvalue, right value),右边的值,是指表达式结束后就不再存在的临时对象。
|
||||||
|
|
||||||
而 C++11 中为了引入强大的右值引用,将右值的概念进行了进一步的划分,分为:纯右值、将亡值。
|
而 C++11 中为了引入强大的右值引用,将右值的概念进行了进一步的划分,分为:纯右值、将亡值。
|
||||||
|
|
||||||
**纯右值(prvalue, pure rvalue)**,纯粹的右值,要么是纯粹的字面量,例如 `10`, `true`;
|
**纯右值** (prvalue, pure rvalue),纯粹的右值,要么是纯粹的字面量,例如 `10`, `true`;
|
||||||
要么是求值结果相当于字面量或匿名临时对象,例如 `1+2`。非引用返回的临时变量、运算表达式产生的临时变量、
|
要么是求值结果相当于字面量或匿名临时对象,例如 `1+2`。非引用返回的临时变量、运算表达式产生的临时变量、
|
||||||
原始字面量、Lambda 表达式都属于纯右值。
|
原始字面量、Lambda 表达式都属于纯右值。
|
||||||
|
|
||||||
**将亡值(xvalue, expiring value)**,是 C++11 为了引入右值引用而提出的概念(因此在传统 C++中,
|
需要注意的是,字面量除了字符串字面量以外,均为纯右值。而字符串字面量是一个左值,类型为 `const char` 数组。例如:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 正确,"01234" 类型为 const char [6],因此是左值
|
||||||
|
const char (&left)[6] = "01234";
|
||||||
|
|
||||||
|
// 断言正确,确实是 const char [6] 类型,注意 decltype(expr) 在 expr 是左值
|
||||||
|
// 且非无括号包裹的 id 表达式与类成员表达式时,会返回左值引用
|
||||||
|
static_assert(std::is_same<decltype("01234"), const char(&)[6]>::value, "");
|
||||||
|
|
||||||
|
// 错误,"01234" 是左值,不可被右值引用
|
||||||
|
// const char (&&right)[6] = "01234";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
但是注意,数组可以被隐式转换成相对应的指针类型,而转换表达式的结果(如果不是左值引用)则一定是个右值(右值引用为将亡值,否则为纯右值)。例如:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const char* p = "01234"; // 正确,"01234" 被隐式转换为 const char*
|
||||||
|
const char*&& pr = "01234"; // 正确,"01234" 被隐式转换为 const char*,该转换的结果是纯右值
|
||||||
|
// const char*& pl = "01234"; // 错误,此处不存在 const char* 类型的左值
|
||||||
|
```
|
||||||
|
|
||||||
|
**将亡值** (xvalue, expiring value),是 C++11 为了引入右值引用而提出的概念(因此在传统 C++ 中,
|
||||||
纯右值和右值是同一个概念),也就是即将被销毁、却能够被移动的值。
|
纯右值和右值是同一个概念),也就是即将被销毁、却能够被移动的值。
|
||||||
|
|
||||||
将亡值可能稍有些难以理解,我们来看这样的代码:
|
将亡值可能稍有些难以理解,我们来看这样的代码:
|
||||||
@@ -240,7 +264,7 @@ std::vector<int> v = foo();
|
|||||||
```
|
```
|
||||||
|
|
||||||
在这样的代码中,就传统的理解而言,函数 `foo` 的返回值 `temp` 在内部创建然后被赋值给 `v`,
|
在这样的代码中,就传统的理解而言,函数 `foo` 的返回值 `temp` 在内部创建然后被赋值给 `v`,
|
||||||
然而 `v` 获得这个对象时,会将整个 temp 拷贝一份,然后把 `temp` 销毁,如果这个 `temp` 非常大,
|
然而 `v` 获得这个对象时,会将整个 `temp` 拷贝一份,然后把 `temp` 销毁,如果这个 `temp` 非常大,
|
||||||
这将造成大量额外的开销(这也就是传统 C++ 一直被诟病的问题)。在最后一行中,`v` 是左值、
|
这将造成大量额外的开销(这也就是传统 C++ 一直被诟病的问题)。在最后一行中,`v` 是左值、
|
||||||
`foo()` 返回的值就是右值(也是纯右值)。但是,`v` 可以被别的变量捕获到,
|
`foo()` 返回的值就是右值(也是纯右值)。但是,`v` 可以被别的变量捕获到,
|
||||||
而 `foo()` 产生的那个返回值作为一个临时值,一旦被 `v` 复制后,将立即被销毁,无法获取、也不能修改。
|
而 `foo()` 产生的那个返回值作为一个临时值,一旦被 `v` 复制后,将立即被销毁,无法获取、也不能修改。
|
||||||
@@ -252,7 +276,7 @@ std::vector<int> v = foo();
|
|||||||
|
|
||||||
### 右值引用和左值引用
|
### 右值引用和左值引用
|
||||||
|
|
||||||
需要拿到一个将亡值,就需要用到右值引用的申明:`T &&`,其中 `T` 是类型。
|
要拿到一个将亡值,就需要用到右值引用:`T &&`,其中 `T` 是类型。
|
||||||
右值引用的声明让这个临时值的生命周期得以延长、只要变量还活着,那么将亡值将继续存活。
|
右值引用的声明让这个临时值的生命周期得以延长、只要变量还活着,那么将亡值将继续存活。
|
||||||
|
|
||||||
C++11 提供了 `std::move` 这个方法将左值参数无条件的转换为右值,
|
C++11 提供了 `std::move` 这个方法将左值参数无条件的转换为右值,
|
||||||
@@ -278,7 +302,7 @@ int main()
|
|||||||
|
|
||||||
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"; // 合法, 非常量引用能够修改临时变量
|
||||||
@@ -318,7 +342,7 @@ void foo() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
由于 `int&` 不能引用 `double` 类型的参数,因此必须产生一个临时值来保存 `s` 的值,
|
由于 `int&` 不能引用 `double` 类型的参数,因此必须产生一个临时值来保存 `s` 的值,
|
||||||
从而当 `increase()` 修改这个临时值时,从而调用完成后 `s` 本身并没有被修改。
|
从而当 `increase()` 修改这个临时值时,调用完成后 `s` 本身并没有被修改。
|
||||||
|
|
||||||
第二个问题,为什么常量引用允许绑定到非左值?原因很简单,因为 Fortran 需要。
|
第二个问题,为什么常量引用允许绑定到非左值?原因很简单,因为 Fortran 需要。
|
||||||
|
|
||||||
@@ -337,19 +361,19 @@ void foo() {
|
|||||||
class A {
|
class A {
|
||||||
public:
|
public:
|
||||||
int *pointer;
|
int *pointer;
|
||||||
A():pointer(new int(1)) {
|
A():pointer(new int(1)) {
|
||||||
std::cout << "构造" << pointer << std::endl;
|
std::cout << "构造" << pointer << std::endl;
|
||||||
}
|
}
|
||||||
A(A& a):pointer(new int(*a.pointer)) {
|
A(A& a):pointer(new int(*a.pointer)) {
|
||||||
std::cout << "拷贝" << pointer << std::endl;
|
std::cout << "拷贝" << pointer << std::endl;
|
||||||
} // 无意义的对象拷贝
|
} // 无意义的对象拷贝
|
||||||
A(A&& a):pointer(a.pointer) {
|
A(A&& a):pointer(a.pointer) {
|
||||||
a.pointer = nullptr;
|
a.pointer = nullptr;
|
||||||
std::cout << "移动" << pointer << std::endl;
|
std::cout << "移动" << pointer << std::endl;
|
||||||
}
|
}
|
||||||
~A(){
|
~A(){
|
||||||
std::cout << "析构" << pointer << std::endl;
|
std::cout << "析构" << pointer << std::endl;
|
||||||
delete pointer;
|
delete pointer;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 防止编译器优化
|
// 防止编译器优化
|
||||||
@@ -382,8 +406,8 @@ int main() {
|
|||||||
|
|
||||||
int main() {
|
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);
|
||||||
@@ -470,7 +494,7 @@ void pass(T&& v) {
|
|||||||
std::cout << " std::forward 传参: ";
|
std::cout << " std::forward 传参: ";
|
||||||
reference(std::forward<T>(v));
|
reference(std::forward<T>(v));
|
||||||
std::cout << "static_cast<T&&> 传参: ";
|
std::cout << "static_cast<T&&> 传参: ";
|
||||||
reference(std::forward<T>(v));
|
reference(static_cast<T&&>(v));
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
std::cout << "传递右值:" << std::endl;
|
std::cout << "传递右值:" << std::endl;
|
||||||
@@ -499,8 +523,8 @@ static_cast<T&&> 传参: 右值引用
|
|||||||
static_cast<T&&> 传参: 左值引用
|
static_cast<T&&> 传参: 左值引用
|
||||||
```
|
```
|
||||||
|
|
||||||
无论传递参数为左值还是右值,普通传参都会将参数作为左值进行转发,
|
无论传递参数为左值还是右值,普通传参都会将参数作为左值进行转发;
|
||||||
所以 `std::move` 总会接受到一个左值,从而转发调用了`reference(int&&)` 输出右值引用。
|
由于类似的原因,`std::move` 总会接受到一个左值,从而转发调用了`reference(int&&)` 输出右值引用。
|
||||||
|
|
||||||
唯独 `std::forward` 即没有造成任何多余的拷贝,同时**完美转发**(传递)了函数的实参给了内部调用的其他函数。
|
唯独 `std::forward` 即没有造成任何多余的拷贝,同时**完美转发**(传递)了函数的实参给了内部调用的其他函数。
|
||||||
|
|
||||||
@@ -526,15 +550,15 @@ constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcep
|
|||||||
```
|
```
|
||||||
|
|
||||||
在这份实现中,`std::remove_reference` 的功能是消除类型中的引用,
|
在这份实现中,`std::remove_reference` 的功能是消除类型中的引用,
|
||||||
而 `std::is_lvalue_reference` 用于检查类型推导是否正确,在 `std::forward` 的第二个实现中
|
`std::is_lvalue_reference` 则用于检查类型推导是否正确,在 `std::forward` 的第二个实现中
|
||||||
检查了接收到的值确实是一个左值,进而体现了坍缩规则。
|
检查了接收到的值确实是一个左值,进而体现了坍缩规则。
|
||||||
|
|
||||||
当 `std::forward` 接受左值时,`_Tp` 被推导为左值,而所以返回值为左值;而当其接受右值时,
|
当 `std::forward` 接受左值时,`_Tp` 被推导为左值,所以返回值为左值;而当其接受右值时,
|
||||||
`_Tp` 被推导为 右值引用,则基于坍缩规则,返回值便成为了 `&& + &&` 的右值。
|
`_Tp` 被推导为 右值引用,则基于坍缩规则,返回值便成为了 `&& + &&` 的右值。
|
||||||
可见 `std::forward` 的原理在于巧妙的利用了模板类型推导中产生的差异。
|
可见 `std::forward` 的原理在于巧妙的利用了模板类型推导中产生的差异。
|
||||||
|
|
||||||
这时我们能回答这样一个问题:为什么在使用循环语句的过程中,`auto&&` 是最安全的方式?
|
这时我们能回答这样一个问题:为什么在使用循环语句的过程中,`auto&&` 是最安全的方式?
|
||||||
因为当 `auto` 被推到为不同的左右引用时,与 `&&` 的坍缩组合是完美转发。
|
因为当 `auto` 被推导为不同的左右引用时,与 `&&` 的坍缩组合是完美转发。
|
||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
@@ -552,6 +576,6 @@ constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcep
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ std::sort(arr.begin(), arr.end());
|
|||||||
而无序容器中的元素是不进行排序的,内部通过 Hash 表实现,插入和搜索元素的平均复杂度为 `O(constant)`,
|
而无序容器中的元素是不进行排序的,内部通过 Hash 表实现,插入和搜索元素的平均复杂度为 `O(constant)`,
|
||||||
在不关心容器内部元素顺序时,能够获得显著的性能提升。
|
在不关心容器内部元素顺序时,能够获得显著的性能提升。
|
||||||
|
|
||||||
C++11 引入了两组无序容器:`std::unordered_map`/`std::unordered_multimap` 和
|
C++11 引入了的两组无序容器分别是:`std::unordered_map`/`std::unordered_multimap` 和
|
||||||
`std::unordered_set`/`std::unordered_multiset`。
|
`std::unordered_set`/`std::unordered_multiset`。
|
||||||
|
|
||||||
它们的用法和原有的 `std::map`/`std::multimap`/`std::set`/`set::multiset` 基本类似,
|
它们的用法和原有的 `std::map`/`std::multimap`/`std::set`/`set::multiset` 基本类似,
|
||||||
@@ -186,14 +186,14 @@ Key:[3] Value:[3]
|
|||||||
|
|
||||||
auto get_student(int id)
|
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 会出现推断错误, 编译失败
|
||||||
}
|
}
|
||||||
@@ -292,7 +292,7 @@ auto tuple_len(T &tpl) {
|
|||||||
// 迭代
|
// 迭代
|
||||||
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(new_tuple, i) << std::endl;
|
||||||
```
|
```
|
||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
@@ -305,6 +305,6 @@ for(int i = 0; i != tuple_len(new_tuple); ++i)
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ order: 5
|
|||||||
|
|
||||||
凡事都有例外,我们总会有需要将对象在自由存储上分配的需求,在传统 C++ 里我们只好使用 `new` 和 `delete` 去
|
凡事都有例外,我们总会有需要将对象在自由存储上分配的需求,在传统 C++ 里我们只好使用 `new` 和 `delete` 去
|
||||||
『记得』对资源进行释放。而 C++11 引入了智能指针的概念,使用了引用计数的想法,让程序员不再需要关心手动释放内存。
|
『记得』对资源进行释放。而 C++11 引入了智能指针的概念,使用了引用计数的想法,让程序员不再需要关心手动释放内存。
|
||||||
这些智能指针就包括 `std::shared_ptr`/`std::unique_ptr`/`std::weak_ptr`,使用它们需要包含头文件 `<memory>`。
|
这些智能指针包括 `std::shared_ptr`/`std::unique_ptr`/`std::weak_ptr`,使用它们需要包含头文件 `<memory>`。
|
||||||
|
|
||||||
> 注意:引用计数不是垃圾回收,引用计数能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待,
|
> 注意:引用计数不是垃圾回收,引用计数能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待,
|
||||||
> 更能够清晰明确的表明资源的生命周期。
|
> 更能够清晰明确的表明资源的生命周期。
|
||||||
|
|
||||||
## 5.2 `std::shared_ptr`
|
## 5.2 `std::shared_ptr`
|
||||||
|
|
||||||
`std::shared_ptr` 是一种智能指针,它能够记录多少个 `shared_ptr` 共同指向一个对象,从而消除显示的调用
|
`std::shared_ptr` 是一种智能指针,它能够记录多少个 `shared_ptr` 共同指向一个对象,从而消除显式的调用
|
||||||
`delete`,当引用计数变为零的时候就会将对象自动删除。
|
`delete`,当引用计数变为零的时候就会将对象自动删除。
|
||||||
|
|
||||||
但还不够,因为使用 `std::shared_ptr` 仍然需要使用 `new` 来调用,这使得代码出现了某种程度上的不对称。
|
但还不够,因为使用 `std::shared_ptr` 仍然需要使用 `new` 来调用,这使得代码出现了某种程度上的不对称。
|
||||||
@@ -38,12 +38,10 @@ order: 5
|
|||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#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); // illegal, no direct assignment
|
// auto pointer = new int(10); // illegal, no direct assignment
|
||||||
// Constructed a std::shared_ptr
|
// Constructed a std::shared_ptr
|
||||||
auto pointer = std::make_shared<int>(10);
|
auto pointer = std::make_shared<int>(10);
|
||||||
@@ -61,21 +59,23 @@ int main()
|
|||||||
auto pointer = std::make_shared<int>(10);
|
auto pointer = std::make_shared<int>(10);
|
||||||
auto pointer2 = pointer; // 引用计数+1
|
auto pointer2 = pointer; // 引用计数+1
|
||||||
auto pointer3 = pointer; // 引用计数+1
|
auto pointer3 = pointer; // 引用计数+1
|
||||||
int *p = pointer.get(); // 这样不会增加引用计数
|
int *p = pointer.get(); // 这样不会增加引用计数
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.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; // 3
|
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
|
||||||
|
|
||||||
pointer2.reset();
|
pointer2.reset();
|
||||||
std::cout << "reset pointer2:" << std::endl;
|
std::cout << "reset pointer2:" << std::endl;
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 已 reset
|
std::cout << "pointer2.use_count() = "
|
||||||
|
<< pointer2.use_count() << std::endl; // pointer2 已 reset; 0
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
|
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
|
||||||
pointer3.reset();
|
pointer3.reset();
|
||||||
std::cout << "reset pointer3:" << std::endl;
|
std::cout << "reset pointer3:" << std::endl;
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
|
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 已 reset
|
std::cout << "pointer3.use_count() = "
|
||||||
|
<< pointer3.use_count() << std::endl; // pointer3 已 reset; 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5.3 `std::unique_ptr`
|
## 5.3 `std::unique_ptr`
|
||||||
@@ -148,20 +148,20 @@ 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::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
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::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
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;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ int main() {
|
|||||||
|
|
||||||
在上图中,最后一步只剩下 B,而 B 并没有任何智能指针引用它,因此这块内存资源也会被释放。
|
在上图中,最后一步只剩下 B,而 B 并没有任何智能指针引用它,因此这块内存资源也会被释放。
|
||||||
|
|
||||||
`std::weak_ptr` 没有 `*` 运算符和 `->` 运算符,所以不能够对资源进行操作,它的唯一作用就是用于检查 `std::shared_ptr` 是否存在,其 `expired()` 方法能在资源未被释放时,会返回 `true`,否则返回 `false`。
|
`std::weak_ptr` 没有 `*` 运算符和 `->` 运算符,所以不能够对资源进行操作,它可以用于检查 `std::shared_ptr` 是否存在,其 `expired()` 方法能在资源未被释放时,会返回 `false`,否则返回 `true`;除此之外,它也可以用于获取指向原始对象的 `std::shared_ptr` 指针,其 `lock()` 方法在原始对象未被释放时,返回一个指向原始对象的 `std::shared_ptr` 指针,进而访问原始对象的资源,否则返回`nullptr`。
|
||||||
|
|
||||||
## 总结
|
## 总结
|
||||||
|
|
||||||
@@ -185,10 +185,10 @@ int main() {
|
|||||||
|
|
||||||
## 进一步阅读的参考资料
|
## 进一步阅读的参考资料
|
||||||
|
|
||||||
1. [stackoverflow 上关于『C++11为什么没有 make_unique』的讨论](http://stackoverflow.com/questions/12580432/why-does-c11-have-make-shared-but-not-make-unique)
|
1. [stackoverflow 上关于『C++11为什么没有 make_unique』的讨论](https://stackoverflow.com/questions/12580432/why-does-c11-have-make-shared-but-not-make-unique)
|
||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ order: 6
|
|||||||
| `\`| 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, `n` 匹配字符 `n`。`\n` 匹配换行符。序列 `\\` 匹配 `'\'` 字符,而 `\(` 则匹配 `'('` 字符。|
|
| `\`| 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, `n` 匹配字符 `n`。`\n` 匹配换行符。序列 `\\` 匹配 `'\'` 字符,而 `\(` 则匹配 `'('` 字符。|
|
||||||
|`^`| 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。|
|
|`^`| 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。|
|
||||||
|`{`| 标记限定符表达式的开始。|
|
|`{`| 标记限定符表达式的开始。|
|
||||||
|`\`| 指明两项之间的一个选择。|
|
|`\|`| 指明两项之间的一个选择。|
|
||||||
|
|
||||||
### 限定符
|
### 限定符
|
||||||
|
|
||||||
@@ -94,7 +94,8 @@ C++11 提供的正则表达式库操作 `std::string` 对象,
|
|||||||
|
|
||||||
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;
|
||||||
@@ -103,7 +104,7 @@ int main() {
|
|||||||
|
|
||||||
另一种常用的形式就是依次传入 `std::string`/`std::smatch`/`std::regex` 三个参数,
|
另一种常用的形式就是依次传入 `std::string`/`std::smatch`/`std::regex` 三个参数,
|
||||||
其中 `std::smatch` 的本质其实是 `std::match_results`。
|
其中 `std::smatch` 的本质其实是 `std::match_results`。
|
||||||
在标准库中, `std::smatch` 被定义为了 `std::match_results<std::string::const_iterator>`,
|
故而在标准库的实现中, `std::smatch` 被定义为了 `std::match_results<std::string::const_iterator>`,
|
||||||
也就是一个子串迭代器类型的 `match_results`。
|
也就是一个子串迭代器类型的 `match_results`。
|
||||||
使用 `std::smatch` 可以方便的对匹配的结果进行获取,例如:
|
使用 `std::smatch` 可以方便的对匹配的结果进行获取,例如:
|
||||||
|
|
||||||
@@ -193,16 +194,23 @@ protected:
|
|||||||
template<typename SERVER_TYPE>
|
template<typename SERVER_TYPE>
|
||||||
void start_server(SERVER_TYPE &server) {
|
void start_server(SERVER_TYPE &server) {
|
||||||
|
|
||||||
// process GET request for /match/[digit+numbers], e.g. GET request is /match/abc123, will return abc123
|
// process GET request for /match/[digit+numbers],
|
||||||
server.resource["fill_your_reg_ex"]["GET"] = [](ostream& response, Request& request) {
|
// e.g. GET request is /match/abc123, will return abc123
|
||||||
|
server.resource["fill_your_reg_ex"]["GET"] =
|
||||||
|
[](ostream& response, Request& request)
|
||||||
|
{
|
||||||
string number=request.path_match[1];
|
string number=request.path_match[1];
|
||||||
response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n" << number;
|
response << "HTTP/1.1 200 OK\r\nContent-Length: "
|
||||||
|
<< number.length() << "\r\n\r\n" << number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// peocess default GET request; anonymous function will be called if no other matches
|
// peocess default GET request;
|
||||||
// response files in folder web/
|
// anonymous function will be called
|
||||||
|
// if no other matches response files in folder web/
|
||||||
// default: index.html
|
// default: index.html
|
||||||
server.default_resource["fill_your_reg_ex"]["GET"] = [](ostream& response, Request& request) {
|
server.default_resource["fill_your_reg_ex"]["GET"] =
|
||||||
|
[](ostream& response, Request& request)
|
||||||
|
{
|
||||||
string filename = "www/";
|
string filename = "www/";
|
||||||
|
|
||||||
string path = request.path_match[1];
|
string path = request.path_match[1];
|
||||||
@@ -230,11 +238,11 @@ void start_server(SERVER_TYPE &server) {
|
|||||||
|
|
||||||
## 进一步阅读的参考资料
|
## 进一步阅读的参考资料
|
||||||
|
|
||||||
1. [知乎『如何评价 GCC 的 C++11 正则表达式?』中原库作者 Tim Shen 的回答](http://zhihu.com/question/23070203/answer/84248248)
|
1. [知乎『如何评价 GCC 的 C++11 正则表达式?』中原库作者 Tim Shen 的回答](https://zhihu.com/question/23070203/answer/84248248)
|
||||||
2. [正则表达式库文档](http://en.cppreference.com/w/cpp/regex)
|
2. [正则表达式库文档](https://en.cppreference.com/w/cpp/regex)
|
||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ order: 7
|
|||||||
## 7.1 并行基础
|
## 7.1 并行基础
|
||||||
|
|
||||||
`std::thread` 用于创建一个执行的线程实例,所以它是一切并发编程的基础,使用时需要包含 `<thread>` 头文件,
|
`std::thread` 用于创建一个执行的线程实例,所以它是一切并发编程的基础,使用时需要包含 `<thread>` 头文件,
|
||||||
它提供了很多基本的线程操作,例如 `get_id()` 来获取所创建线程的线程 ID,使用 `join()` 来加入一个线程等等,例如:
|
它提供了很多基本的线程操作,例如 `get_id()` 来获取所创建线程的线程 ID,使用 `join()` 来等待一个线程结束(与该线程汇合)等等,例如:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -31,17 +31,19 @@ int main() {
|
|||||||
我们在操作系统、亦或是数据库的相关知识中已经了解过了有关并发技术的基本知识,`mutex` 就是其中的核心之一。
|
我们在操作系统、亦或是数据库的相关知识中已经了解过了有关并发技术的基本知识,`mutex` 就是其中的核心之一。
|
||||||
C++11 引入了 `mutex` 相关的类,其所有相关的函数都放在 `<mutex>` 头文件中。
|
C++11 引入了 `mutex` 相关的类,其所有相关的函数都放在 `<mutex>` 头文件中。
|
||||||
|
|
||||||
`std::mutex` 是 C++11 中最基本的 `mutex` 类,通过实例化 `std::mutex` 可以创建互斥量,
|
`std::mutex` 是 C++11 中最基本的互斥量类,可以通过构造 `std::mutex` 对象创建互斥量,
|
||||||
而通过其成员函数 `lock()` 可以进行上锁,`unlock()` 可以进行解锁。
|
而通过其成员函数 `lock()` 可以进行上锁,`unlock()` 可以进行解锁。
|
||||||
但是在在实际编写代码的过程中,最好不去直接调用成员函数,
|
但是在实际编写代码的过程中,最好不去直接调用成员函数,
|
||||||
因为调用成员函数就需要在每个临界区的出口处调用 `unlock()`,当然,还包括异常。
|
因为调用成员函数就需要在每个临界区的出口处调用 `unlock()`,当然,还包括异常。
|
||||||
这时候 C++11 还为互斥量提供了一个 RAII 语法的模板类 `std::lock_gurad`。
|
而 C++11 为互斥量提供了一个 RAII 机制的模板类 `std::lock_guard`。
|
||||||
|
|
||||||
RAII 在不失代码简洁性的同时,很好的保证了代码的异常安全性。
|
RAII 在不失代码简洁性的同时,很好的保证了代码的异常安全性。
|
||||||
|
|
||||||
在 RAII 用法下,对于临界区的互斥量的创建只需要在作用域的开始部分,例如:
|
在 RAII 用法下,对于临界区的互斥量的创建只需要在作用域的开始部分,例如:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
int v = 1;
|
int v = 1;
|
||||||
@@ -66,10 +68,12 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
由于 C++ 保证了所有栈对象在声明周期结束时会被销毁,所以这样的代码也是异常安全的。
|
由于 C++ 保证了所有栈对象在生命周期结束时会被销毁,所以这样的代码也是异常安全的。
|
||||||
无论 `critical_section()` 正常返回、还是在中途抛出异常,都会引发堆栈回退,也就自动调用了 `unlock()`。
|
无论 `critical_section()` 正常返回、还是在中途抛出异常,都会引发栈回溯,也就自动调用了 `unlock()`。
|
||||||
|
|
||||||
而 `std::unique_lock` 则相对于 `std::lock_guard` 出现的,`std::unique_lock` 更加灵活,
|
> 没有捕获抛出的异常(此时由实现定义是否进行栈回溯)。
|
||||||
|
|
||||||
|
而 `std::unique_lock` 则是相对于 `std::lock_guard` 出现的,`std::unique_lock` 更加灵活,
|
||||||
`std::unique_lock` 的对象会以独占所有权(没有其他的 `unique_lock` 对象同时拥有某个 `mutex` 对象的所有权)
|
`std::unique_lock` 的对象会以独占所有权(没有其他的 `unique_lock` 对象同时拥有某个 `mutex` 对象的所有权)
|
||||||
的方式管理 `mutex` 对象上的上锁和解锁的操作。所以在并发编程中,推荐使用 `std::unique_lock`。
|
的方式管理 `mutex` 对象上的上锁和解锁的操作。所以在并发编程中,推荐使用 `std::unique_lock`。
|
||||||
|
|
||||||
@@ -82,6 +86,7 @@ int main() {
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
int v = 1;
|
int v = 1;
|
||||||
@@ -145,7 +150,8 @@ int main() {
|
|||||||
std::cout << "waiting...";
|
std::cout << "waiting...";
|
||||||
result.wait(); // 在此设置屏障,阻塞到期物的完成
|
result.wait(); // 在此设置屏障,阻塞到期物的完成
|
||||||
// 输出执行结果
|
// 输出执行结果
|
||||||
std::cout << "done!" << std:: endl << "future result is " << result.get() << std::endl;
|
std::cout << "done!" << std:: endl << "future result is "
|
||||||
|
<< result.get() << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -157,7 +163,7 @@ int main() {
|
|||||||
条件变量 `std::condition_variable` 是为了解决死锁而生,当互斥操作不够用而引入的。
|
条件变量 `std::condition_variable` 是为了解决死锁而生,当互斥操作不够用而引入的。
|
||||||
比如,线程可能需要等待某个条件为真才能继续执行,
|
比如,线程可能需要等待某个条件为真才能继续执行,
|
||||||
而一个忙等待循环中可能会导致所有其他线程都无法进入临界区使得条件为真时,就会发生死锁。
|
而一个忙等待循环中可能会导致所有其他线程都无法进入临界区使得条件为真时,就会发生死锁。
|
||||||
所以,`condition_variable` 实例被创建出现主要就是用于唤醒等待线程从而避免死锁。
|
所以,`condition_variable` 对象被创建出现主要就是用于唤醒等待线程从而避免死锁。
|
||||||
`std::condition_variable`的 `notify_one()` 用于唤醒一个线程;
|
`std::condition_variable`的 `notify_one()` 用于唤醒一个线程;
|
||||||
`notify_all()` 则是通知所有线程。下面是一个生产者和消费者模型的例子:
|
`notify_all()` 则是通知所有线程。下面是一个生产者和消费者模型的例子:
|
||||||
|
|
||||||
@@ -196,7 +202,8 @@ int main() {
|
|||||||
}
|
}
|
||||||
// 短暂取消锁,使得生产者有机会在消费者消费空前继续生产
|
// 短暂取消锁,使得生产者有机会在消费者消费空前继续生产
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 消费者慢于生产者
|
// 消费者慢于生产者
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
lock.lock();
|
lock.lock();
|
||||||
while (!produced_nums.empty()) {
|
while (!produced_nums.empty()) {
|
||||||
std::cout << "consuming " << produced_nums.front() << std::endl;
|
std::cout << "consuming " << produced_nums.front() << std::endl;
|
||||||
@@ -228,7 +235,7 @@ int main() {
|
|||||||
## 7.5 原子操作与内存模型
|
## 7.5 原子操作与内存模型
|
||||||
|
|
||||||
细心的读者可能会对前一小节中生产者消费者模型的例子可能存在编译器优化导致程序出错的情况产生疑惑。
|
细心的读者可能会对前一小节中生产者消费者模型的例子可能存在编译器优化导致程序出错的情况产生疑惑。
|
||||||
例如,布尔值 `notified` 没有被 `volatile` 修饰,编译器可能对此变量存在优化,例如将其作为一个寄存器的值,
|
例如,编译器可能对变量 `notified` 存在优化,例如将其作为一个寄存器的值,
|
||||||
从而导致消费者线程永远无法观察到此值的变化。这是一个好问题,为了解释清楚这个问题,我们需要进一步讨论
|
从而导致消费者线程永远无法观察到此值的变化。这是一个好问题,为了解释清楚这个问题,我们需要进一步讨论
|
||||||
从 C++ 11 起引入的内存模型这一概念。我们首先来看一个问题,下面这段代码输出结果是多少?
|
从 C++ 11 起引入的内存模型这一概念。我们首先来看一个问题,下面这段代码输出结果是多少?
|
||||||
|
|
||||||
@@ -238,7 +245,7 @@ int main() {
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
int a = 0;
|
int a = 0;
|
||||||
int flag = 0;
|
volatile int flag = 0;
|
||||||
|
|
||||||
std::thread t1([&]() {
|
std::thread t1([&]() {
|
||||||
while (flag != 1);
|
while (flag != 1);
|
||||||
@@ -258,10 +265,10 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
从直观上看,`t2` 中 `a = 5;` 这一条语句似乎总在 `flag = 1;` 之前得到执行,而 `t1` 中 `while (flag != 1)`
|
从直观上看,`t2` 中 `a = 5;` 这一条语句似乎总在 `flag = 1;` 之前得到执行,而 `t1` 中 `while (flag != 1)`
|
||||||
似乎保证了 `std::cout << "b = " << b << std::endl;` 不会再标记被改变前执行。从逻辑上看,似乎 `b` 的值应该等于 5。
|
似乎保证了 `std::cout << "b = " << b << std::endl;` 不会再标记被改变前执行。从逻辑上看,似乎 `b` 的值应该等于 5。
|
||||||
但实际情况远比此复杂得多,或者说这段代码本身属于未定义的行为,因为对于 `a` 和 `flag` 而言,他们在两个并行的线程中被读写,
|
但实际情况远比此复杂得多,或者说这段代码本身属于未定义的行为,因为对于 `a` 和 `flag` 而言,他们在两个并行的线程中被读写,
|
||||||
出现了竞争。除此之外,即便我们忽略竞争读写,仍然可能收 CPU 的乱序执行,编译器对指令的重排的影响,
|
出现了竞争。除此之外,即便我们忽略竞争读写,仍然可能受 CPU 的乱序执行,编译器对指令的重排的影响,
|
||||||
导致 `a = 5` 发生在 `flag = 1` 之后。从而 `b` 可能输出 0。
|
导致 `a = 5` 发生在 `flag = 1` 之后。从而 `b` 可能输出 0。
|
||||||
|
|
||||||
### 原子操作
|
### 原子操作
|
||||||
@@ -275,9 +282,8 @@ int main() {
|
|||||||
这是一组非常强的同步条件,换句话说当最终编译为 CPU 指令时会表现为非常多的指令(我们之后再来看如何实现一个简单的互斥锁)。
|
这是一组非常强的同步条件,换句话说当最终编译为 CPU 指令时会表现为非常多的指令(我们之后再来看如何实现一个简单的互斥锁)。
|
||||||
这对于一个仅需原子级操作(没有中间态)的变量,似乎太苛刻了。
|
这对于一个仅需原子级操作(没有中间态)的变量,似乎太苛刻了。
|
||||||
|
|
||||||
关于同步条件的研究有着非常久远的历史,我们在这里不进行赘述。读者应该明白,在现代 CPU 体系结构下提供了 CPU 指令级的原子操作,
|
关于同步条件的研究有着非常久远的历史,我们在这里不进行赘述。读者应该明白,现代 CPU 体系结构提供了 CPU 指令级的原子操作,
|
||||||
因此在 C++11 中多线程下共享变量的读写这一问题上,还引入了 `std::atomic` 模板,使得我们实例化一个原子类型,将一个
|
因此在多线程下共享变量的读写这一问题上, C++11 中还引入了 `std::atomic` 模板,使得我们能实例化原子类型,并将一个原子写操作从一组指令,最小化到单个 CPU 指令。例如:
|
||||||
原子类型读写操作从一组指令,最小化到单个 CPU 指令。例如:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::atomic<int> counter;
|
std::atomic<int> counter;
|
||||||
@@ -309,7 +315,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
当然,并非所有的类型都能提供原子操作,这是因为原子操作的可行性取决于 CPU 的架构以及所实例化的类型结构是否满足该架构对内存对齐
|
当然,并非所有的类型都能提供原子操作,这是因为原子操作的可行性取决于具体的 CPU 架构,以及所实例化的类型结构是否能够满足该 CPU 架构对内存对齐
|
||||||
条件的要求,因而我们总是可以通过 `std::atomic<T>::is_lock_free` 来检查该原子类型是否需支持原子操作,例如:
|
条件的要求,因而我们总是可以通过 `std::atomic<T>::is_lock_free` 来检查该原子类型是否需支持原子操作,例如:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -321,6 +327,8 @@ struct A {
|
|||||||
int y;
|
int y;
|
||||||
long long z;
|
long long z;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
std::atomic<A> a;
|
std::atomic<A> a;
|
||||||
std::cout << std::boolalpha << a.is_lock_free() << std::endl;
|
std::cout << std::boolalpha << a.is_lock_free() << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -404,7 +412,7 @@ struct A {
|
|||||||
y.load() c = a + b x.store(3)
|
y.load() c = a + b x.store(3)
|
||||||
```
|
```
|
||||||
|
|
||||||
上面给出的三种例子都是属于因果一致的,因为整个过程中,只有 `c` 对 `a` 和 `b` 产生依赖,而 `x` 和 `y`
|
上面给出的三种例子都是属于因果一致的,因为整个过程中,只有 `c` 对 `a` 和 `b` 产生依赖,而 `x` 和 `y`
|
||||||
在此例子中表现为没有关系(但实际情况中我们需要更详细的信息才能确定 `x` 与 `y` 确实无关)
|
在此例子中表现为没有关系(但实际情况中我们需要更详细的信息才能确定 `x` 与 `y` 确实无关)
|
||||||
|
|
||||||
4. 最终一致性:是最弱的一致性要求,它只保障某个操作在未来的某个时间节点上会被观察到,但并未要求被观察到的时间。因此我们甚至可以对此条件稍作加强,例如规定某个操作被观察到的时间总是有界的。当然这已经不在我们的讨论范围之内了。
|
4. 最终一致性:是最弱的一致性要求,它只保障某个操作在未来的某个时间节点上会被观察到,但并未要求被观察到的时间。因此我们甚至可以对此条件稍作加强,例如规定某个操作被观察到的时间总是有界的。当然这已经不在我们的讨论范围之内了。
|
||||||
@@ -415,7 +423,7 @@ struct A {
|
|||||||
|
|
||||||
|
|
||||||
T2 ---------+------------+--------------------+--------+-------->
|
T2 ---------+------------+--------------------+--------+-------->
|
||||||
x.read() x.read() x.read() x.read()
|
x.read x.read() x.read() x.read()
|
||||||
```
|
```
|
||||||
|
|
||||||
在上面的情况中,如果我们假设 x 的初始值为 0,则 `T2` 中四次 `x.read()` 结果可能但不限于以下情况:
|
在上面的情况中,如果我们假设 x 的初始值为 0,则 `T2` 中四次 `x.read()` 结果可能但不限于以下情况:
|
||||||
@@ -424,7 +432,8 @@ struct A {
|
|||||||
3 4 4 4 // x 的写操作被很快观察到
|
3 4 4 4 // x 的写操作被很快观察到
|
||||||
0 3 3 4 // x 的写操作被观察到的时间存在一定延迟
|
0 3 3 4 // x 的写操作被观察到的时间存在一定延迟
|
||||||
0 0 0 4 // 最后一次读操作读到了 x 的最终值,但此前的变化并未观察到
|
0 0 0 4 // 最后一次读操作读到了 x 的最终值,但此前的变化并未观察到
|
||||||
0 0 0 0 // 在当前时间段内 x 的写操作均未被观察到,但未来某个时间点上一定能观察到 x 为 4 的情况
|
0 0 0 0 // 在当前时间段内 x 的写操作均未被观察到,
|
||||||
|
// 但未来某个时间点上一定能观察到 x 为 4 的情况
|
||||||
```
|
```
|
||||||
|
|
||||||
### 内存顺序
|
### 内存顺序
|
||||||
@@ -437,7 +446,7 @@ struct A {
|
|||||||
std::atomic<int> counter = {0};
|
std::atomic<int> counter = {0};
|
||||||
std::vector<std::thread> vt;
|
std::vector<std::thread> vt;
|
||||||
for (int i = 0; i < 100; ++i) {
|
for (int i = 0; i < 100; ++i) {
|
||||||
vt.emplace_back([](){
|
vt.emplace_back([&](){
|
||||||
counter.fetch_add(1, std::memory_order_relaxed);
|
counter.fetch_add(1, std::memory_order_relaxed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -451,7 +460,8 @@ struct A {
|
|||||||
2. 释放/消费模型:在此模型中,我们开始限制进程间的操作顺序,如果某个线程需要修改某个值,但另一个线程会对该值的某次操作产生依赖,即后者依赖前者。具体而言,线程 A 完成了三次对 `x` 的写操作,线程 `B` 仅依赖其中第三次 `x` 的写操作,与 `x` 的前两次写行为无关,则当 `A` 主动 `x.release()` 时候(即使用 `std::memory_order_release`),选项 `std::memory_order_consume` 能够确保 `B` 在调用 `x.load()` 时候观察到 `A` 中第三次对 `x` 的写操作。我们来看一个例子:
|
2. 释放/消费模型:在此模型中,我们开始限制进程间的操作顺序,如果某个线程需要修改某个值,但另一个线程会对该值的某次操作产生依赖,即后者依赖前者。具体而言,线程 A 完成了三次对 `x` 的写操作,线程 `B` 仅依赖其中第三次 `x` 的写操作,与 `x` 的前两次写行为无关,则当 `A` 主动 `x.release()` 时候(即使用 `std::memory_order_release`),选项 `std::memory_order_consume` 能够确保 `B` 在调用 `x.load()` 时候观察到 `A` 中第三次对 `x` 的写操作。我们来看一个例子:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::atomic<int*> ptr;
|
// 初始化为 nullptr 防止 consumer 线程从野指针进行读取
|
||||||
|
std::atomic<int*> ptr(nullptr);
|
||||||
int v;
|
int v;
|
||||||
std::thread producer([&]() {
|
std::thread producer([&]() {
|
||||||
int* p = new int(42);
|
int* p = new int(42);
|
||||||
@@ -469,9 +479,9 @@ struct A {
|
|||||||
consumer.join();
|
consumer.join();
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 释放/获取模型:在此模型下,我们可以进一步加紧对不同线程间原子操作的顺序的限制,在释放 `std::memory_order_release` 和获取 `std::memory_order_acquire` 之间规定时序,即发生在释放操作之前的**所有**写操作,对其他线程的任何获取操作都是可见的,亦即发生顺序(happens-before)。
|
3. 释放/获取模型:在此模型下,我们可以进一步加紧对不同线程间原子操作的顺序的限制,在释放 `std::memory_order_release` 和获取 `std::memory_order_acquire` 之间规定时序,即发生在释放(release)操作之前的**所有**写操作,对其他线程的任何获取(acquire)操作都是可见的,亦即发生顺序(happens-before)。
|
||||||
|
|
||||||
可以看到,`std::memory_order_release` 确保了它之后的写行为不会发生在释放操作之前,是一个向后的屏障,而 `std::memory_order_acquire` 确保了它之后的前的写行为,不会发生在该获取操作之后,是一个向前的屏障,对于选项 `std::memory_order_acq_rel` 而言,则结合了这两者的特点,唯一确定了一个内存屏障,使得当前线程对内存的读写不会被重排到此操作的前后。
|
可以看到,`std::memory_order_release` 确保了它之前的写操作不会发生在释放操作之后,是一个向后的屏障(backward),而 `std::memory_order_acquire` 确保了它之前的写行为不会发生在该获取操作之后,是一个向前的屏障(forward)。对于选项 `std::memory_order_acq_rel` 而言,则结合了这两者的特点,唯一确定了一个内存屏障,使得当前线程对内存的读写不会被重排并越过此操作的前后:
|
||||||
|
|
||||||
我们来看一个例子:
|
我们来看一个例子:
|
||||||
|
|
||||||
@@ -484,9 +494,8 @@ struct A {
|
|||||||
});
|
});
|
||||||
std::thread acqrel([&]() {
|
std::thread acqrel([&]() {
|
||||||
int expected = 1; // must before compare_exchange_strong
|
int expected = 1; // must before compare_exchange_strong
|
||||||
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) {
|
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel))
|
||||||
expected = 1; // must after compare_exchange_strong
|
expected = 1; // must after compare_exchange_strong
|
||||||
}
|
|
||||||
// flag has changed to 2
|
// flag has changed to 2
|
||||||
});
|
});
|
||||||
std::thread acquire([&]() {
|
std::thread acquire([&]() {
|
||||||
@@ -499,7 +508,7 @@ struct A {
|
|||||||
acquire.join();
|
acquire.join();
|
||||||
```
|
```
|
||||||
|
|
||||||
在此例中我们使用了 `compare_exchange_strong`,它便是比较交换原语(Compare-and-swap primitive),它有一个更弱的版本,即 `compare_exchange_weak`,它允许即便交换成功,也仍然返回 `false` 失败。其原因是因为在某些平台上虚假故障导致的,具体而言,当 CPU 进行上下文切换时,另一线程加载同一地址产生的不一致。除此之外,`compare_exchange_strong` 的性能可能稍差于 `compare_exchange_weak`,但大部分情况下,`compare_exchange_strong` 应该被有限考虑。
|
在此例中我们使用了 `compare_exchange_strong` 比较交换原语(Compare-and-swap primitive),它有一个更弱的版本,即 `compare_exchange_weak`,它允许即便交换成功,也仍然返回 `false` 失败。其原因是因为在某些平台上虚假故障导致的,具体而言,当 CPU 进行上下文切换时,另一线程加载同一地址产生的不一致。除此之外,`compare_exchange_strong` 的性能可能稍差于 `compare_exchange_weak`,但大部分情况下,鉴于其使用的复杂度而言,`compare_exchange_weak` 应该被有限考虑。
|
||||||
|
|
||||||
4. 顺序一致模型:在此模型下,原子操作满足顺序一致性,进而可能对性能产生损耗。可显式的通过 `std::memory_order_seq_cst` 进行指定。最后来看一个例子:
|
4. 顺序一致模型:在此模型下,原子操作满足顺序一致性,进而可能对性能产生损耗。可显式的通过 `std::memory_order_seq_cst` 进行指定。最后来看一个例子:
|
||||||
|
|
||||||
@@ -507,7 +516,7 @@ struct A {
|
|||||||
std::atomic<int> counter = {0};
|
std::atomic<int> counter = {0};
|
||||||
std::vector<std::thread> vt;
|
std::vector<std::thread> vt;
|
||||||
for (int i = 0; i < 100; ++i) {
|
for (int i = 0; i < 100; ++i) {
|
||||||
vt.emplace_back([](){
|
vt.emplace_back([&](){
|
||||||
counter.fetch_add(1, std::memory_order_seq_cst);
|
counter.fetch_add(1, std::memory_order_seq_cst);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -548,12 +557,12 @@ C++11 语言层提供了并发编程的相关支持,本节简单的介绍了 `
|
|||||||
|
|
||||||
## 进一步阅读的参考资料
|
## 进一步阅读的参考资料
|
||||||
|
|
||||||
- [C++ 并发编程\(中文版\)](https://www.gitbook.com/book/chenxiaowei/cpp_concurrency_in_action/details)
|
- [C++ 并发编程\(中文版\)](https://book.douban.com/subject/26386925/)
|
||||||
- [线程支持库文档](http://en.cppreference.com/w/cpp/thread)
|
- [线程支持库文档](https://en.cppreference.com/w/cpp/thread)
|
||||||
- Herlihy, M. P., & Wing, J. M. (1990). Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems, 12(3), 463–492. https://doi.org/10.1145/78969.78972
|
- Herlihy, M. P., & Wing, J. M. (1990). Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems, 12(3), 463–492. https://doi.org/10.1145/78969.78972
|
||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ TODO:
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ int main()
|
|||||||
try {
|
try {
|
||||||
may_throw();
|
may_throw();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
std::cout << "捕获异常, 来自 my_throw()" << std::endl;
|
std::cout << "捕获异常, 来自 may_throw()" << std::endl;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
non_block_throw();
|
non_block_throw();
|
||||||
@@ -89,7 +89,7 @@ try {
|
|||||||
最终输出为:
|
最终输出为:
|
||||||
|
|
||||||
```
|
```
|
||||||
捕获异常, 来自 my_throw()
|
捕获异常, 来自 may_throw()
|
||||||
捕获异常, 来自 non_block_throw()
|
捕获异常, 来自 non_block_throw()
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -187,6 +187,6 @@ int main() {
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -89,11 +89,11 @@ TODO:
|
|||||||
## 进一步阅读的参考资料
|
## 进一步阅读的参考资料
|
||||||
|
|
||||||
- [Why Concepts didn't make C++17?](http://honermann.net/blog/2016/03/06/why-concepts-didnt-make-cxx17/)
|
- [Why Concepts didn't make C++17?](http://honermann.net/blog/2016/03/06/why-concepts-didnt-make-cxx17/)
|
||||||
- [C++11/14/17/20 编译器支持情况](http://en.cppreference.com/w/cpp/compiler_support)
|
- [C++11/14/17/20 编译器支持情况](https://en.cppreference.com/w/cpp/compiler_support)
|
||||||
- [C++ 历史](https://en.cppreference.com/w/cpp/language/history)
|
- [C++ 历史](https://en.cppreference.com/w/cpp/language/history)
|
||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
@@ -10,7 +10,7 @@ order: 11
|
|||||||
|
|
||||||
正如本书引言部分提到的,本书只是一本带你快速领略现代 C++ 11/14/17/20 新特性的读物,而非进阶学习实践 C++『黑魔法』的内容。笔者当然也想到了这个需求,只是这样的内容非常艰深,鲜有受众。在此,笔者列出一些能够帮助你在此书基础之上进一步学习现代 C++ 的资料,希望能够祝你一臂之力:
|
正如本书引言部分提到的,本书只是一本带你快速领略现代 C++ 11/14/17/20 新特性的读物,而非进阶学习实践 C++『黑魔法』的内容。笔者当然也想到了这个需求,只是这样的内容非常艰深,鲜有受众。在此,笔者列出一些能够帮助你在此书基础之上进一步学习现代 C++ 的资料,希望能够祝你一臂之力:
|
||||||
|
|
||||||
- [C++ 参考](http://en.cppreference.com/w)
|
- [C++ 参考](https://en.cppreference.com/w)
|
||||||
- [CppCon YouTube 频道](https://www.youtube.com/user/CppCon/videos)
|
- [CppCon YouTube 频道](https://www.youtube.com/user/CppCon/videos)
|
||||||
- [Ulrich Drepper. 每位程序员都需要知道的内存知识. 2007](https://people.freebsd.org/~lstewart/articles/cpumemory.pdf)
|
- [Ulrich Drepper. 每位程序员都需要知道的内存知识. 2007](https://people.freebsd.org/~lstewart/articles/cpumemory.pdf)
|
||||||
- 待补充
|
- 待补充
|
||||||
@@ -19,6 +19,6 @@ order: 11
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
@@ -36,6 +36,6 @@ TODO:
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
@@ -109,6 +109,6 @@
|
|||||||
|
|
||||||
## 许可
|
## 许可
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ int main() {
|
|||||||
std::cout << "NULL == nullptr" << std::endl;
|
std::cout << "NULL == nullptr" << std::endl;
|
||||||
|
|
||||||
foo(0); // will call foo(int)
|
foo(0); // will call foo(int)
|
||||||
// foo(NULL); // doen't compile
|
// foo(NULL); // doesn't compile
|
||||||
foo(nullptr); // will call foo(char*)
|
foo(nullptr); // will call foo(char*)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,9 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// wrong
|
int add(auto x, auto y) { // Supported in C++20
|
||||||
// int add(auto x, auto y) {
|
return x+y;
|
||||||
// return x+y;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
||||||
@@ -34,10 +33,11 @@ int main() {
|
|||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
auto i = 5; // type int
|
auto i = 5; // type int
|
||||||
auto j = 6; // type int
|
auto j = 6; // type int
|
||||||
auto arr = new auto(10); // type int*
|
std::cout << add(i, j) << std::endl;
|
||||||
// auto auto_arr2[10] = arr;
|
|
||||||
// std::cout << add(i, j) << std::endl;
|
auto arr = new auto(10); // type int*
|
||||||
|
// auto auto_arr2[10] = {arr}; // invalid
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <utility>
|
#include <memory> // std::make_unique
|
||||||
|
#include <utility> // std::move
|
||||||
|
|
||||||
void lambda_value_capture() {
|
void lambda_value_capture() {
|
||||||
int value = 1;
|
int value = 1;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ void reference(std::string&& str) {
|
|||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
std::string lv1 = "string,"; // lv1 is a lvalue
|
std::string lv1 = "string,"; // lv1 is a lvalue
|
||||||
// std::string&& r1 = s1; // illegal, rvalue can't ref to lvalue
|
// std::string&& r1 = lv1; // illegal, rvalue can't ref to lvalue
|
||||||
std::string&& rv1 = std::move(lv1); // legal, std::move can convert lvalue to rvalue
|
std::string&& rv1 = std::move(lv1); // legal, std::move can convert lvalue to rvalue
|
||||||
std::cout << rv1 << std::endl; // string,
|
std::cout << rv1 << std::endl; // string,
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ int main() {
|
|||||||
|
|
||||||
// p2 is empty, no prints
|
// p2 is empty, no prints
|
||||||
if(p2) p2->foo();
|
if(p2) p2->foo();
|
||||||
std::cout << "p2 was destroied" << std::endl;
|
std::cout << "p2 was destroyed" << std::endl;
|
||||||
}
|
}
|
||||||
// p1 is not empty, prints
|
// p1 is not empty, prints
|
||||||
if (p1) p1->foo();
|
if (p1) p1->foo();
|
||||||
|
|
||||||
// Foo instance will be destroied when leaving the scope
|
// Foo instance will be destroyed when leaving the scope
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ class A {
|
|||||||
public:
|
public:
|
||||||
std::shared_ptr<B> pointer;
|
std::shared_ptr<B> pointer;
|
||||||
~A() {
|
~A() {
|
||||||
std::cout << "A was destroied" << std::endl;
|
std::cout << "A was destroyed" << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
class B {
|
class B {
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<A> pointer;
|
std::shared_ptr<A> pointer;
|
||||||
~B() {
|
~B() {
|
||||||
std::cout << "B was destroied" << std::endl;
|
std::cout << "B was destroyed" << std::endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
int main() {
|
int main() {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
int v = 1;
|
int v = 1;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
int v = 1;
|
int v = 1;
|
||||||
|
|||||||
@@ -15,15 +15,15 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
atomic<int> counter = {0};
|
|
||||||
const int N = 10000;
|
const int N = 10000;
|
||||||
|
|
||||||
void relaxed_order() {
|
void relaxed_order() {
|
||||||
cout << "relaxed_order: " << endl;
|
cout << "relaxed_order: " << endl;
|
||||||
|
|
||||||
|
atomic<int> counter = {0};
|
||||||
vector<thread> vt;
|
vector<thread> vt;
|
||||||
for (int i = 0; i < N; ++i) {
|
for (int i = 0; i < N; ++i) {
|
||||||
vt.emplace_back([](){
|
vt.emplace_back([&](){
|
||||||
counter.fetch_add(1, memory_order_relaxed);
|
counter.fetch_add(1, memory_order_relaxed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -86,9 +86,10 @@ void release_acquire_order() {
|
|||||||
void sequential_consistent_order() {
|
void sequential_consistent_order() {
|
||||||
cout << "sequential_consistent_order: " << endl;
|
cout << "sequential_consistent_order: " << endl;
|
||||||
|
|
||||||
|
atomic<int> counter = {0};
|
||||||
vector<thread> vt;
|
vector<thread> vt;
|
||||||
for (int i = 0; i < N; ++i) {
|
for (int i = 0; i < N; ++i) {
|
||||||
vt.emplace_back([](){
|
vt.emplace_back([&](){
|
||||||
counter.fetch_add(1, memory_order_seq_cst);
|
counter.fetch_add(1, memory_order_seq_cst);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
FROM node:latest
|
FROM node:latest
|
||||||
|
|
||||||
LABEL "maintainer"="Changkun Ou <hi@changkun.us>"
|
LABEL "maintainer"="Changkun Ou <hi[at]changkun.de>"
|
||||||
LABEL "repository"="https://github.com/changkun/modern-cpp-tutorial"
|
LABEL "repository"="https://github.com/changkun/modern-cpp-tutorial"
|
||||||
LABEL "homepage"="https://changkun.de/modern-cpp/"
|
LABEL "homepage"="https://changkun.de/modern-cpp/"
|
||||||
|
|
||||||
# FUCKING UNICODE
|
# For Unicode
|
||||||
ENV LANG C.UTF-8
|
ENV LANG C.UTF-8
|
||||||
ENV LC_ALL C.UTF-8
|
ENV LC_ALL C.UTF-8
|
||||||
|
|
||||||
|
|||||||
51
epub/en-us/Makefile
Normal file
51
epub/en-us/Makefile
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
title = 'Modern C++ Tutorial: C++11/14/17/20 On the Fly'
|
||||||
|
filename = 'modern-cpp-tutorial'
|
||||||
|
outputname='modern-cpp-tutorial'
|
||||||
|
revision = $(shell git describe --always --tags)
|
||||||
|
date = $(shell date +'%Y.%m.%d-%H:%M')
|
||||||
|
all: revision epub
|
||||||
|
|
||||||
|
revision:
|
||||||
|
@echo '---' >> meta.markdown
|
||||||
|
@echo 'title: "Modern C++ Tutorial: C++11/14/17/20 On the Fly"' >> meta.markdown
|
||||||
|
@echo 'author: Changkun Ou <hi[at]changkun.de>' >> meta.markdown
|
||||||
|
@echo 'subtitle: |' >> meta.markdown
|
||||||
|
@echo ' The content in this PDF file may outdated, please check our website <https://changkun.de/modern-cpp> or GitHub repository <https://github.com/changkun/modern-cpp-tutorial> for the latest book updates. Last update: ${date}' >> meta.markdown
|
||||||
|
@echo 'rights: © Ou Changkun, CC BY-NC-ND 4.0.' >> meta.markdown
|
||||||
|
@echo 'ibooks:' >> meta.markdown
|
||||||
|
@echo ' - version: ${revision}' >> meta.markdown
|
||||||
|
@echo '---' >> meta.markdown
|
||||||
|
|
||||||
|
epub: markdown
|
||||||
|
@echo "Compiling PDF file..."
|
||||||
|
pandoc -f markdown+smart --toc -t epub -o $(filename).epub \
|
||||||
|
--highlight-style haddock \
|
||||||
|
--epub-cover-image ../../assets/cover-2nd-en.png \
|
||||||
|
--title-prefix $(title) \
|
||||||
|
meta.markdown \
|
||||||
|
00-preface.md.markdown \
|
||||||
|
01-intro.md.markdown \
|
||||||
|
02-usability.md.markdown \
|
||||||
|
03-runtime.md.markdown \
|
||||||
|
04-containers.md.markdown \
|
||||||
|
05-pointers.md.markdown \
|
||||||
|
06-regex.md.markdown \
|
||||||
|
07-thread.md.markdown \
|
||||||
|
08-filesystem.md.markdown \
|
||||||
|
09-others.md.markdown \
|
||||||
|
10-cpp20.md.markdown \
|
||||||
|
appendix1.md.markdown \
|
||||||
|
appendix2.md.markdown
|
||||||
|
@echo "Done."
|
||||||
|
rm -f *.md *.markdown
|
||||||
|
|
||||||
|
markdown:
|
||||||
|
@echo "Copy markdown files..."
|
||||||
|
cp -r ../../book/en-us/* .
|
||||||
|
@echo "Aggregating markdown files..."
|
||||||
|
python3 filter.py
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.md *.markdown
|
||||||
|
|
||||||
|
.PHONY: markdown epub clean
|
||||||
19
epub/en-us/filter.py
Normal file
19
epub/en-us/filter.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# !/usr/bin/env python3
|
||||||
|
# author: changkun<hi[at]changkun.de>
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
chapters = ['00-preface.md', '01-intro.md', '02-usability.md', '03-runtime.md', '04-containers.md', '05-pointers.md', '06-regex.md', '07-thread.md', '08-filesystem.md', '09-others.md', '10-cpp20.md', 'appendix1.md', 'appendix2.md']
|
||||||
|
|
||||||
|
ignores = ['TOC', 'Table of Content', 'License', 'license']
|
||||||
|
|
||||||
|
for chapter in chapters:
|
||||||
|
with open(chapter+'.markdown', 'w') as outfile:
|
||||||
|
if os.path.isfile(chapter):
|
||||||
|
with open(chapter) as ch:
|
||||||
|
outfile.write('\n')
|
||||||
|
for line in ch:
|
||||||
|
if any(keyword in line for keyword in ignores):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
outfile.write(line)
|
||||||
51
epub/zh-cn/Makefile
Normal file
51
epub/zh-cn/Makefile
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
title = '现代 C++ 教程:高速上手 C++11/14/17/20'
|
||||||
|
filename = 'modern-cpp-tutorial'
|
||||||
|
outputname='modern-cpp-tutorial'
|
||||||
|
revision = $(shell git describe --always --tags)
|
||||||
|
date = $(shell date +'%Y.%m.%d-%H:%M')
|
||||||
|
all: revision epub
|
||||||
|
|
||||||
|
revision:
|
||||||
|
@echo '---' >> meta.markdown
|
||||||
|
@echo 'title: "现代 C++ 教程:高速上手 C++11/14/17/20"' >> meta.markdown
|
||||||
|
@echo 'author: 欧长坤 <hi[at]changkun.de>' >> meta.markdown
|
||||||
|
@echo 'subtitle: |' >> meta.markdown
|
||||||
|
@echo ' 此文件的内容可能过期,请检查本书网站 <https://changkun.de/modern-cpp> 及 GitHub 仓库<https://github.com/changkun/modern-cpp-tutorial> 以获取最新内容。最后更新:${date}' >> meta.markdown
|
||||||
|
@echo 'rights: © Ou Changkun, CC BY-NC-ND 4.0.' >> meta.markdown
|
||||||
|
@echo 'ibooks:' >> meta.markdown
|
||||||
|
@echo ' - version: ${revision}' >> meta.markdown
|
||||||
|
@echo '---' >> meta.markdown
|
||||||
|
|
||||||
|
epub: markdown
|
||||||
|
@echo "Compiling PDF file..."
|
||||||
|
pandoc -f markdown+smart --toc -t epub -o $(filename).epub \
|
||||||
|
--highlight-style haddock \
|
||||||
|
--epub-cover-image ../../assets/cover-2nd.png \
|
||||||
|
--title-prefix $(title) \
|
||||||
|
meta.markdown \
|
||||||
|
00-preface.md.markdown \
|
||||||
|
01-intro.md.markdown \
|
||||||
|
02-usability.md.markdown \
|
||||||
|
03-runtime.md.markdown \
|
||||||
|
04-containers.md.markdown \
|
||||||
|
05-pointers.md.markdown \
|
||||||
|
06-regex.md.markdown \
|
||||||
|
07-thread.md.markdown \
|
||||||
|
08-filesystem.md.markdown \
|
||||||
|
09-others.md.markdown \
|
||||||
|
10-cpp20.md.markdown \
|
||||||
|
appendix1.md.markdown \
|
||||||
|
appendix2.md.markdown
|
||||||
|
@echo "Done."
|
||||||
|
rm -f *.md *.markdown
|
||||||
|
|
||||||
|
markdown:
|
||||||
|
@echo "Copy markdown files..."
|
||||||
|
cp -r ../../book/zh-cn/* .
|
||||||
|
@echo "Aggregating markdown files..."
|
||||||
|
python3 filter.py
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.md *.markdown
|
||||||
|
|
||||||
|
.PHONY: markdown epub clean
|
||||||
19
epub/zh-cn/filter.py
Normal file
19
epub/zh-cn/filter.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# !/usr/bin/env python3
|
||||||
|
# author: changkun<hi[at]changkun.de>
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
chapters = ['00-preface.md', '01-intro.md', '02-usability.md', '03-runtime.md', '04-containers.md', '05-pointers.md', '06-regex.md', '07-thread.md', '08-filesystem.md', '09-others.md', '10-cpp20.md', 'appendix1.md', 'appendix2.md']
|
||||||
|
|
||||||
|
ignores = ['TOC', '返回目录', '许可', 'license']
|
||||||
|
|
||||||
|
for chapter in chapters:
|
||||||
|
with open(chapter+'.markdown', 'w') as outfile:
|
||||||
|
if os.path.isfile(chapter):
|
||||||
|
with open(chapter) as ch:
|
||||||
|
outfile.write('\n')
|
||||||
|
for line in ch:
|
||||||
|
if any(keyword in line for keyword in ignores):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
outfile.write(line)
|
||||||
@@ -109,7 +109,7 @@ decltype(auto) ThreadPool::enqueue(F&& f, Args&&... args) {
|
|||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex);
|
std::unique_lock<std::mutex> lock(queue_mutex);
|
||||||
|
|
||||||
// avoid add new thread if theadpool is destroied
|
// avoid add new thread if theadpool is destroyed
|
||||||
if(stop)
|
if(stop)
|
||||||
throw std::runtime_error("enqueue on stopped ThreadPool");
|
throw std::runtime_error("enqueue on stopped ThreadPool");
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ revision = $(shell git describe --always --tags)
|
|||||||
all: revision pdf
|
all: revision pdf
|
||||||
|
|
||||||
revision:
|
revision:
|
||||||
@echo '% Autogenerated, do not edit' > revision.tex
|
printf '%% Autogenerated, do not edit\n' > revision.tex
|
||||||
@echo '\\newcommand{\\revision}{'$(revision)'}' >> revision.tex
|
printf '\\newcommand{\\revision}{'$(revision)'}' >> revision.tex
|
||||||
|
|
||||||
pdf: markdown
|
pdf: markdown
|
||||||
@echo "Compiling PDF file..."
|
@echo "Compiling PDF file..."
|
||||||
pandoc -f markdown+smart -s $(filename).md -o $(filename).pdf \
|
pandoc -f markdown+smart -s $(filename).md -o $(filename).pdf \
|
||||||
--title-prefix $(title) \
|
--title-prefix $(title) \
|
||||||
--listings -H meta/cpp-listings.tex \
|
|
||||||
--template=meta/template.tex \
|
--template=meta/template.tex \
|
||||||
--pdf-engine=`which xelatex`
|
--pdf-engine=`which xelatex`
|
||||||
@echo "Done."
|
@echo "Done."
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# !/usr/bin/env python3
|
# !/usr/bin/env python3
|
||||||
# author: changkun<hi@changkun.us>
|
# author: changkun<hi[at]changkun.de>
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ ignores = ['TOC', 'Table of Content', 'License', 'license']
|
|||||||
with open('modern-cpp-tutorial.md', 'w') as outfile:
|
with open('modern-cpp-tutorial.md', 'w') as outfile:
|
||||||
outfile.write("""---
|
outfile.write("""---
|
||||||
title: "Modern C++ Tutorial: C++11/14/17/20 On the Fly"
|
title: "Modern C++ Tutorial: C++11/14/17/20 On the Fly"
|
||||||
author: Changkun Ou <hi@changkun.us>
|
author: Changkun Ou <hi[at]changkun.de>
|
||||||
copyright: cc-by-nc-nd 4.0
|
copyright: cc-by-nc-nd 4.0
|
||||||
---
|
---
|
||||||
""")
|
""")
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
\usepackage{xcolor}
|
|
||||||
|
|
||||||
\definecolor{keyword}{HTML}{BA2CA3}
|
|
||||||
\definecolor{string}{HTML}{D12F1B}
|
|
||||||
\definecolor{comment}{HTML}{008400}
|
|
||||||
|
|
||||||
\lstset{
|
|
||||||
basicstyle={\small\ttfamily},
|
|
||||||
keywordstyle={\color[rgb]{0.13,0.29,0.53}\bfseries},
|
|
||||||
breaklines=true,
|
|
||||||
emphstyle={\bfseries\color{Rhodamine}},
|
|
||||||
commentstyle={\color[rgb]{0.56,0.35,0.01}\itshape},
|
|
||||||
stringstyle={\color[rgb]{0.31,0.60,0.02}},
|
|
||||||
showstringspaces=false,
|
|
||||||
frame=shadowbox,
|
|
||||||
breakatwhitespace=false,
|
|
||||||
captionpos=b,
|
|
||||||
extendedchars=true,
|
|
||||||
keepspaces=true,
|
|
||||||
numbers=left,
|
|
||||||
numberstyle=\tiny,
|
|
||||||
rulecolor=\color{black},
|
|
||||||
rulesepcolor={\color{blue!20!white}},
|
|
||||||
showspaces=false,
|
|
||||||
}
|
|
||||||
@@ -47,9 +47,7 @@ $if(biblio-files)$
|
|||||||
\bibliography{$biblio-files$}
|
\bibliography{$biblio-files$}
|
||||||
$endif$
|
$endif$
|
||||||
$endif$
|
$endif$
|
||||||
$if(listings)$
|
|
||||||
\usepackage{listings}
|
\usepackage{listings}
|
||||||
$endif$
|
|
||||||
$if(lhs)$
|
$if(lhs)$
|
||||||
\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
|
\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
|
||||||
$endif$
|
$endif$
|
||||||
@@ -161,7 +159,7 @@ $endif$
|
|||||||
{\LARGE\textbf{Modern C++ Tutorial: C++11/14/17/20 On the Fly}}
|
{\LARGE\textbf{Modern C++ Tutorial: C++11/14/17/20 On the Fly}}
|
||||||
|
|
||||||
\vspace{1em}
|
\vspace{1em}
|
||||||
{\large Changkun Ou (hi@changkun.us)}
|
{\large Changkun Ou (hi[at]changkun.de)}
|
||||||
|
|
||||||
\vspace{1ex}
|
\vspace{1ex}
|
||||||
Last update: \today
|
Last update: \today
|
||||||
@@ -174,7 +172,7 @@ $endif$
|
|||||||
\vspace{1em}
|
\vspace{1em}
|
||||||
\textbf{\large License}
|
\textbf{\large License}
|
||||||
|
|
||||||
\noindent This work was written by \href{https://github.com/changkun}{Ou Changkun} and licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. \texttt{\small http://creativecommons.org/licenses/by-nc-nd/4.0/}
|
\noindent This work was written by \href{https://github.com/changkun}{Ou Changkun} and licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. \texttt{\small https://creativecommons.org/licenses/by-nc-nd/4.0/}
|
||||||
|
|
||||||
\vspace{5em}
|
\vspace{5em}
|
||||||
\includegraphics{../../assets/cover-2nd-en}
|
\includegraphics{../../assets/cover-2nd-en}
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ revision = $(shell git describe --always --tags)
|
|||||||
all: revision pdf
|
all: revision pdf
|
||||||
|
|
||||||
revision:
|
revision:
|
||||||
@echo '% Autogenerated, do not edit' > revision.tex
|
printf '%% Autogenerated, do not edit\n' > revision.tex
|
||||||
@echo '\\newcommand{\\revision}{'$(revision)'}' >> revision.tex
|
printf '\\newcommand{\\revision}{'$(revision)'}' >> revision.tex
|
||||||
|
|
||||||
pdf: markdown
|
pdf: markdown
|
||||||
@echo "Compiling PDF file..."
|
@echo "Compiling PDF file..."
|
||||||
pandoc -f markdown+smart -s $(filename).md -o $(filename).pdf \
|
pandoc -f markdown+smart -s $(filename).md -o $(filename).pdf \
|
||||||
--title-prefix $(title) \
|
--title-prefix $(title) \
|
||||||
--listings -H meta/cpp-listings.tex \
|
|
||||||
--template=meta/template.tex \
|
--template=meta/template.tex \
|
||||||
--pdf-engine=`which xelatex`
|
--pdf-engine=`which xelatex`
|
||||||
@echo "Done."
|
@echo "Done."
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# !/usr/bin/env python3
|
# !/usr/bin/env python3
|
||||||
# author: changkun<hi@changkun.us>
|
# author: changkun<hi[at]changkun.de>
|
||||||
|
|
||||||
import os, io
|
import os, io
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ ignores = ['TOC', '返回目录', '许可', 'license']
|
|||||||
|
|
||||||
head = """---
|
head = """---
|
||||||
title: "现代 C++ 教程:高速上手 C++11/14/17/20"
|
title: "现代 C++ 教程:高速上手 C++11/14/17/20"
|
||||||
author: 欧长坤 <hi@changkun.us>
|
author: 欧长坤 <hi[at]changkun.de>
|
||||||
copyright: cc-by-nc-nd 4.0
|
copyright: cc-by-nc-nd 4.0
|
||||||
---
|
---
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
\usepackage{xcolor}
|
|
||||||
|
|
||||||
\definecolor{keyword}{HTML}{BA2CA3}
|
|
||||||
\definecolor{string}{HTML}{D12F1B}
|
|
||||||
\definecolor{comment}{HTML}{008400}
|
|
||||||
|
|
||||||
\lstset{
|
|
||||||
basicstyle={\small\ttfamily},
|
|
||||||
keywordstyle={\color[rgb]{0.13,0.29,0.53}\bfseries},
|
|
||||||
breaklines=true,
|
|
||||||
emphstyle={\bfseries\color{Rhodamine}},
|
|
||||||
commentstyle={\color[rgb]{0.56,0.35,0.01}\itshape},
|
|
||||||
stringstyle={\color[rgb]{0.31,0.60,0.02}},
|
|
||||||
showstringspaces=false,
|
|
||||||
frame=shadowbox,
|
|
||||||
breakatwhitespace=false,
|
|
||||||
captionpos=b,
|
|
||||||
extendedchars=true,
|
|
||||||
keepspaces=true,
|
|
||||||
numbers=left,
|
|
||||||
numberstyle=\tiny,
|
|
||||||
rulecolor=\color{black},
|
|
||||||
rulesepcolor={\color{blue!20!white}},
|
|
||||||
showspaces=false,
|
|
||||||
}
|
|
||||||
@@ -51,9 +51,8 @@ $if(biblio-files)$
|
|||||||
\bibliography{$biblio-files$}
|
\bibliography{$biblio-files$}
|
||||||
$endif$
|
$endif$
|
||||||
$endif$
|
$endif$
|
||||||
$if(listings)$
|
\usepackage{textcomp}
|
||||||
\usepackage{listings}
|
\usepackage{listings}
|
||||||
$endif$
|
|
||||||
$if(lhs)$
|
$if(lhs)$
|
||||||
\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
|
\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
|
||||||
$endif$
|
$endif$
|
||||||
@@ -167,7 +166,7 @@ $endif$
|
|||||||
{\LARGE\textbf{现代 C++ 教程:高速上手 C++11/14/17/20}}
|
{\LARGE\textbf{现代 C++ 教程:高速上手 C++11/14/17/20}}
|
||||||
|
|
||||||
\vspace{1em}
|
\vspace{1em}
|
||||||
{\large 欧长坤 (hi@changkun.us)}
|
{\large 欧长坤 (hi[at]changkun.de)}
|
||||||
|
|
||||||
\vspace{1ex}
|
\vspace{1ex}
|
||||||
最后更新 \today - \revision
|
最后更新 \today - \revision
|
||||||
@@ -180,7 +179,7 @@ $endif$
|
|||||||
\vspace{1em}
|
\vspace{1em}
|
||||||
\textbf{\large 版权声明}
|
\textbf{\large 版权声明}
|
||||||
|
|
||||||
\noindent 本书系\href{https://github.com/changkun}{欧长坤}著,采用“知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (cc by-nc-sa)”进行许可。\texttt{\small http://creativecommons.org/licenses/by-nc-nd/4.0/}
|
\noindent 本书系\href{https://github.com/changkun}{欧长坤}著,采用“知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND)”进行许可。\texttt{\small https://creativecommons.org/licenses/by-nc-nd/4.0/}
|
||||||
|
|
||||||
\vspace{7em}
|
\vspace{7em}
|
||||||
\includegraphics{../../assets/cover-2nd}
|
\includegraphics{../../assets/cover-2nd}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ all: clean
|
|||||||
node install.js
|
node install.js
|
||||||
python3 filter.py
|
python3 filter.py
|
||||||
cp ../assets/cover-2nd.png ./src/modern-cpp/assets/cover-2nd.png
|
cp ../assets/cover-2nd.png ./src/modern-cpp/assets/cover-2nd.png
|
||||||
|
cp ../assets/cover-2nd-logo.png ./src/modern-cpp/assets/cover-2nd-logo.png
|
||||||
cp ../assets/cover-2nd-en.png ./src/modern-cpp/assets/cover-2nd-en.png
|
cp ../assets/cover-2nd-en.png ./src/modern-cpp/assets/cover-2nd-en.png
|
||||||
|
cp ../assets/cover-2nd-en-logo.png ./src/modern-cpp/assets/cover-2nd-en-logo.png
|
||||||
cp ../assets/alipay.jpg ./src/modern-cpp/assets/alipay.jpg
|
cp ../assets/alipay.jpg ./src/modern-cpp/assets/alipay.jpg
|
||||||
cp ../assets/wechat.jpg ./src/modern-cpp/assets/wechat.jpg
|
cp ../assets/wechat.jpg ./src/modern-cpp/assets/wechat.jpg
|
||||||
cp ../assets/qq-group.png ./src/modern-cpp/assets/qq-group.png
|
|
||||||
cp ../assets/donate.md ./src/modern-cpp/about/
|
cp ../assets/donate.md ./src/modern-cpp/about/
|
||||||
cp ../assets/community.md ./src/modern-cpp/about/
|
|
||||||
cp -r ../assets/figures ./src/modern-cpp/assets/figures
|
cp -r ../assets/figures ./src/modern-cpp/assets/figures
|
||||||
cp -r ../exercises ./src/modern-cpp/
|
cp -r ../exercises ./src/modern-cpp/
|
||||||
cp -r ../code ./src/modern-cpp/
|
cp -r ../code ./src/modern-cpp/
|
||||||
@@ -17,11 +17,13 @@ s: all
|
|||||||
node_modules/serve/bin/serve.js ./public
|
node_modules/serve/bin/serve.js ./public
|
||||||
clean:
|
clean:
|
||||||
rm -rf ./src/modern-cpp/assets/cover-2nd.png \
|
rm -rf ./src/modern-cpp/assets/cover-2nd.png \
|
||||||
|
./src/modern-cpp/assets/cover-2nd-en.png \
|
||||||
|
./src/modern-cpp/assets/cover-2nd-logo.png \
|
||||||
|
./src/modern-cpp/assets/cover-2nd-en-logo.png \
|
||||||
./src/modern-cpp/assets/figures \
|
./src/modern-cpp/assets/figures \
|
||||||
./src/modern-cpp/assets/alipay.jpg \
|
./src/modern-cpp/assets/alipay.jpg \
|
||||||
./src/modern-cpp/assets/wechat.jpg \
|
./src/modern-cpp/assets/wechat.jpg \
|
||||||
./src/modern-cpp/about/donate.md \
|
./src/modern-cpp/about/donate.md \
|
||||||
./src/modern-cpp/about/community.md \
|
|
||||||
./src/modern-cpp/code \
|
./src/modern-cpp/code \
|
||||||
./src/modern-cpp/exercises \
|
./src/modern-cpp/exercises \
|
||||||
public db.json src/modern-cpp/zh-cn src/modern-cpp/en-us
|
public db.json src/modern-cpp/zh-cn src/modern-cpp/en-us
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ title: "现代 C++ 教程: 高速上手 C++ 11/14/17/20"
|
|||||||
subtitle: C++ 11/14/17/20 On the Fly
|
subtitle: C++ 11/14/17/20 On the Fly
|
||||||
description: "欧长坤"
|
description: "欧长坤"
|
||||||
author: 欧长坤
|
author: 欧长坤
|
||||||
email: hi[at]changkun.us
|
email: hi[at]changkun.de
|
||||||
language: zh-CN
|
language: zh-CN
|
||||||
|
|
||||||
# URL
|
# URL
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# !/usr/bin/env python3
|
# !/usr/bin/env python3
|
||||||
# author: changkun<hi@changkun.us>
|
# author: changkun<hi[at]changkun.de>
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|||||||
6707
website/package-lock.json
generated
6707
website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,17 +3,17 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"hexo": {
|
"hexo": {
|
||||||
"version": "3.8.0"
|
"version": "5.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hexo-generator-index": "^0.2.1",
|
"hexo-generator-index": "^2.0.0",
|
||||||
"hexo-image-caption": "^0.1.1",
|
"hexo-image-caption": "^0.1.1",
|
||||||
"hexo-renderer-ejs": "^0.3.1",
|
"hexo-renderer-ejs": "^1.0.0",
|
||||||
"hexo-renderer-marked": "^1.0.1",
|
"hexo-renderer-marked": "^4.0.0",
|
||||||
"hexo-renderer-stylus": "^0.3.3"
|
"hexo-renderer-stylus": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"hexo": "^3.8.0",
|
"hexo": "^5.3.0",
|
||||||
"serve": ">=7.0.0"
|
"serve": "^13.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
website/src/modern-cpp/about/ack.md
Normal file
31
website/src/modern-cpp/about/ack.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
title: 致谢
|
||||||
|
type: about
|
||||||
|
order: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
This book was originally written in Chinese by [Changkun Ou](https://changkun.de).
|
||||||
|
|
||||||
|
The author has limited time and language skills. If readers find any mistakes in the book or any language improvements, please feel free to open an [Issue](https://github.com/changkun/modern-cpp-tutorial/issues) or start a [Pull request](https://github.com/changkun/modern-cpp-tutorial/pulls). For detailed guidelines and checklist, please refer to [How to contribute](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
The author is grateful to all contributors, including but not limited to [Contributors](https://github.com/changkun/modern-cpp-tutorial/graphs/contributors).
|
||||||
|
|
||||||
|
<p>This project is also supported by:</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://www.digitalocean.com/?refcode=834a3bbc951b&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=CopyPaste">
|
||||||
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## 致谢
|
||||||
|
|
||||||
|
笔者时间和水平有限,如果读者发现书中内容的错误,欢迎提 [Issue](https://github.com/changkun/modern-cpp-tutorial/issues),或者直接提 [Pull request](https://github.com/changkun/modern-cpp-tutorial/pulls)。详细贡献指南请参考[如何参与贡献](CONTRIBUTING.md),由衷感谢每一位指出本书中出现错误的读者,包括但不限于 [Contributors](https://github.com/changkun/modern-cpp-tutorial/graphs/contributors)。
|
||||||
|
|
||||||
|
<p>本项目还由以下产品提供赞助支持:</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://www.digitalocean.com/?refcode=834a3bbc951b&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=CopyPaste">
|
||||||
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
@@ -4,6 +4,6 @@ type: about
|
|||||||
order: 3
|
order: 3
|
||||||
---
|
---
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/80x15.png" /></a>
|
||||||
|
|
||||||
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 [MIT](https://opensource.org/licenses/MIT) 协议开源。
|
本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 [MIT](https://opensource.org/licenses/MIT) 协议开源。
|
||||||
@@ -51,11 +51,11 @@
|
|||||||
<div id="footer">
|
<div id="footer">
|
||||||
<p>
|
<p>
|
||||||
<a href="https://changkun.de">欧长坤</a> © 2016-<%- new Date().getFullYear() %> 版权所有,
|
<a href="https://changkun.de">欧长坤</a> © 2016-<%- new Date().getFullYear() %> 版权所有,
|
||||||
采用<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议许可</a>,代码使用 <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a> 协议开源。</a>
|
采用<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议许可</a>,代码使用 <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a> 协议开源。</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="https://changkun.de">Changkun Ou</a> © 2016-<%- new Date().getFullYear() %>.
|
<a href="https://changkun.de">Changkun Ou</a> © 2016-<%- new Date().getFullYear() %>.
|
||||||
The book is licensed under <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0</a>, code is open sourced under the <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a> License. </a>
|
The book is licensed under <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0</a>, code is open sourced under the <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a> License. </a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<script src="https://cdn.rawgit.com/wnda/pivot/master/pivot.js"></script>
|
<script src="https://cdn.rawgit.com/wnda/pivot/master/pivot.js"></script>
|
||||||
|
|||||||
@@ -10,6 +10,15 @@
|
|||||||
<link rel="shortcut icon" type="image/x-icon" href="/modern-cpp/assets/cover-2nd.png">
|
<link rel="shortcut icon" type="image/x-icon" href="/modern-cpp/assets/cover-2nd.png">
|
||||||
<meta name="msapplication-TileColor" content="#7e2d36">
|
<meta name="msapplication-TileColor" content="#7e2d36">
|
||||||
<meta name="theme-color" content="#7e2d36">
|
<meta name="theme-color" content="#7e2d36">
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-80889616-2"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'UA-80889616-2');
|
||||||
|
</script>
|
||||||
|
|
||||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600|Roboto Mono' rel='stylesheet' type='text/css'>
|
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600|Roboto Mono' rel='stylesheet' type='text/css'>
|
||||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
|
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
|
||||||
@@ -20,7 +29,7 @@
|
|||||||
<!-- this needs to be loaded before guide's inline scripts -->
|
<!-- this needs to be loaded before guide's inline scripts -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
|
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
|
||||||
<script>window.PAGE_TYPE = "<%- page.type %>"</script>
|
<script>window.PAGE_TYPE = "<%- page.type %>"</script>
|
||||||
|
<script async src="//changkun.de/urlstat/client.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="<%- isIndex ? '' : 'docs' -%>">
|
<body class="<%- isIndex ? '' : 'docs' -%>">
|
||||||
<% if (page.type == 'book-en-us') { %>
|
<% if (page.type == 'book-en-us') { %>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p>
|
<p>
|
||||||
<a href="https://changkun.de">Changkun Ou</a> © 2016-<%- new Date().getFullYear() %>.
|
<a href="https://changkun.de">Changkun Ou</a> © 2016-<%- new Date().getFullYear() %>.
|
||||||
The book is licensed under <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0</a>, code is open sourced under the <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a> License. </a>
|
The book is licensed under <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0</a>, code is open sourced under the <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a> License. </a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If you like the book, you could <a href="/modern-cpp/about/donate.html">donate the author</a>.
|
If you like the book, you could <a href="/modern-cpp/about/donate.html">donate the author</a>.
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<p>
|
<p>
|
||||||
<a href="https://changkun.de">欧长坤</a> © 2016-<%- new Date().getFullYear() %> 版权所有,
|
<a href="https://changkun.de">欧长坤</a> © 2016-<%- new Date().getFullYear() %> 版权所有,
|
||||||
采用<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议许可</a>,代码使用 <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a> 协议开源。</a>
|
采用<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/">知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议许可</a>,代码使用 <a href="https://opensource.org/licenses/MIT" target="_blank">MIT</a> 协议开源。</a>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
如果你认为本书对你起到了帮助,可以<a href="/modern-cpp/about/donate.html">资助作者</a>。
|
如果你认为本书对你起到了帮助,可以<a href="/modern-cpp/about/donate.html">资助作者</a>。
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<div id="header">
|
<div id="header">
|
||||||
<a id="logo" href="<%- url_for("/modern-cpp/") %>">
|
<a id="logo" href="<%- url_for("/modern-cpp/") %>">
|
||||||
<% if (page.type == 'book-en-us') { %>
|
<% if (page.type == 'book-en-us') { %>
|
||||||
<img src="<%- url_for("/modern-cpp/assets/cover-2nd-en.png") %>">
|
<img src="<%- url_for("/modern-cpp/assets/cover-2nd-en-logo.png") %>">
|
||||||
<span>Modern C++ Tutorial: C++ 11/14/17/20 On the Fly</span>
|
<span>Modern C++ Tutorial: C++ 11/14/17/20 On the Fly</span>
|
||||||
<% } else {%>
|
<% } else {%>
|
||||||
<img src="<%- url_for("/modern-cpp/assets/cover-2nd.png") %>">
|
<img src="<%- url_for("/modern-cpp/assets/cover-2nd-logo.png") %>">
|
||||||
<span>现代 C++ 教程:高速上手 C++ 11/14/17/20</span>
|
<span>现代 C++ 教程:高速上手 C++ 11/14/17/20</span>
|
||||||
<% } %>
|
<% } %>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
<ul class="nav-dropdown">
|
<ul class="nav-dropdown">
|
||||||
<li><ul>
|
<li><ul>
|
||||||
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/donate.html") %>" >资助</a></li>
|
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/donate.html") %>" >资助</a></li>
|
||||||
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/community.html") %>" >社区</a></li>
|
|
||||||
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/copyright.html") %>" >版权声明</a></li>
|
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/copyright.html") %>" >版权声明</a></li>
|
||||||
|
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/ack.html") %>" >致谢</a></li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
<ul class="nav-dropdown">
|
<ul class="nav-dropdown">
|
||||||
<li><ul>
|
<li><ul>
|
||||||
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/donate.html") %>" >Donate</a></li>
|
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/donate.html") %>" >Donate</a></li>
|
||||||
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/community.html") %>" >Community</a></li>
|
|
||||||
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/copyright.html") %>" >Copyright</a></li>
|
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/copyright.html") %>" >Copyright</a></li>
|
||||||
|
<li><a class="nav-link" href="<%- url_for("/modern-cpp/about/ack.html") %>" >Acknowledgements</a></li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ body.docs
|
|||||||
position: absolute
|
position: absolute
|
||||||
width: 25px
|
width: 25px
|
||||||
height: 30px
|
height: 30px
|
||||||
background: url(../assets/cover-2nd-en.png) center center no-repeat
|
background: url(../assets/cover-2nd-en-logo.png) center center no-repeat
|
||||||
background-size: auto 100%
|
background-size: auto 100%
|
||||||
top: 12px
|
top: 12px
|
||||||
right: 12px
|
right: 12px
|
||||||
|
|||||||
Reference in New Issue
Block a user