Showing preview only (427K chars total). Download the full file or copy to clipboard to get everything.
Repository: changkun/modern-cpp-tutorial
Branch: master
Commit: 01b64d415e91
Files: 160
Total size: 390.8 KB
Directory structure:
gitextract_8bonjra0/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── Bug_report.md
│ │ ├── Feature_request.md
│ │ └── Question.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ └── website.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README-zh-cn.md
├── README.md
├── assets/
│ ├── cover-2nd-en.afphoto
│ ├── cover-2nd.afphoto
│ ├── cover-2nd.psd
│ └── donate.md
├── book/
│ ├── en-us/
│ │ ├── 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
│ │ └── toc.md
│ └── zh-cn/
│ ├── 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
│ ├── appendix3.md
│ └── toc.md
├── code/
│ ├── 1/
│ │ ├── 1.1.c.and.cpp
│ │ ├── Makefile
│ │ ├── foo.c
│ │ └── foo.h
│ ├── 10/
│ │ ├── 10.1.without.concepts.cpp
│ │ ├── 10.2.concepts.cpp
│ │ └── Makefile
│ ├── 2/
│ │ ├── 2.01.nullptr.cpp
│ │ ├── 2.02.constexpr.cpp
│ │ ├── 2.03.if.switch.cpp
│ │ ├── 2.04.initializer.list.cpp
│ │ ├── 2.05.structured.binding.cpp
│ │ ├── 2.06.auto.cpp
│ │ ├── 2.07.decltype.cpp
│ │ ├── 2.08.tail.return.type.cpp
│ │ ├── 2.09.decltype.auto.cpp
│ │ ├── 2.10.if.constexpr.cpp
│ │ ├── 2.11.for.loop.cpp
│ │ ├── 2.12.external.template.cpp
│ │ ├── 2.13.alias.template.cpp
│ │ ├── 2.14.default.template.param.cpp
│ │ ├── 2.15.variadic.template.param.cpp
│ │ ├── 2.16.fold.expression.cpp
│ │ ├── 2.18.non.type.template.auto.cpp
│ │ ├── 2.19.delegate.constructor.cpp
│ │ ├── 2.20.strong.type.enum.cpp
│ │ └── Makefile
│ ├── 3/
│ │ ├── 3.1.lambda.basic.cpp
│ │ ├── 3.2.function.wrap.cpp
│ │ ├── 3.3.rvalue.cpp
│ │ ├── 3.4.historical.cpp
│ │ ├── 3.5.move.semantics.cpp
│ │ ├── 3.6.move.semantics.cpp
│ │ ├── 3.7.perfect.forward.cpp
│ │ └── Makefile
│ ├── 4/
│ │ ├── 4.1.linear.container.cpp
│ │ ├── 4.2.unordered.map.cpp
│ │ ├── 4.3.tuples.cpp
│ │ └── Makefile
│ ├── 5/
│ │ ├── 5.1.shared.ptr.a.cpp
│ │ ├── 5.2.unique.ptr.cpp
│ │ ├── 5.3.weak.ptr.cpp
│ │ └── Makefile
│ ├── 6/
│ │ ├── 6.1.regex.cpp
│ │ └── Makefile
│ ├── 7/
│ │ ├── 7.1.thread.basic.cpp
│ │ ├── 7.2.critical.section.a.cpp
│ │ ├── 7.3.critical.section.b.cpp
│ │ ├── 7.4.futures.cpp
│ │ ├── 7.5.producer.consumer.cpp
│ │ ├── 7.6.atomic.cpp
│ │ ├── 7.6.bad.example.cpp
│ │ ├── 7.7.is.lock.free.cpp
│ │ ├── 7.8.memory.order.cpp
│ │ └── Makefile
│ └── 9/
│ ├── 9.1.noexcept.cpp
│ ├── 9.2.literals.cpp
│ ├── 9.3.alignment.cpp
│ └── Makefile
├── docker/
│ └── Dockerfile
├── epub/
│ ├── en-us/
│ │ ├── Makefile
│ │ └── filter.py
│ └── zh-cn/
│ ├── Makefile
│ └── filter.py
├── exercises/
│ ├── 2/
│ │ ├── fold.expresion.cpp
│ │ └── structured.binding.cpp
│ ├── 6/
│ │ ├── Makefile
│ │ ├── handler.hpp
│ │ ├── main.http.cpp
│ │ ├── main.https.cpp
│ │ ├── server.base.hpp
│ │ ├── server.http.hpp
│ │ ├── server.https.hpp
│ │ └── www/
│ │ ├── index.html
│ │ └── test.html
│ └── 7/
│ ├── 7.1/
│ │ ├── Makefile
│ │ ├── main.cpp
│ │ └── thread_pool.hpp
│ ├── 7.2.mutex.cpp
│ └── Makefile
├── pdf/
│ ├── en-us/
│ │ ├── Makefile
│ │ ├── aggregator.py
│ │ └── meta/
│ │ └── template.tex
│ └── zh-cn/
│ ├── Makefile
│ ├── aggregator.py
│ └── meta/
│ └── template.tex
└── website/
├── Makefile
├── README.md
├── _config.yml
├── filter.py
├── install.js
├── package.json
├── src/
│ ├── _posts/
│ │ └── index.md
│ └── modern-cpp/
│ └── about/
│ ├── ack.md
│ └── copyright.md
└── themes/
└── moderncpp/
├── _config.yml
├── layout/
│ ├── index.ejs
│ ├── layout.ejs
│ ├── page.ejs
│ ├── partials/
│ │ ├── header.ejs
│ │ ├── main_menu.ejs
│ │ ├── main_menu_en.ejs
│ │ ├── sidebar.ejs
│ │ └── toc.ejs
│ └── post.ejs
└── source/
└── modern-cpp/
├── css/
│ ├── _animations.styl
│ ├── _common.styl
│ ├── _header.styl
│ ├── _settings.styl
│ ├── _sidebar.styl
│ ├── _syntax.styl
│ ├── index.styl
│ └── page.styl
└── js/
└── common.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# 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']
================================================
FILE: .github/ISSUE_TEMPLATE/Bug_report.md
================================================
---
name: Bug report
about: 报告错误
---
<!-- English Version -->
## Actual Description
- File Path: for example, `book/en-us/02-usability.md`
- Original paragraph:
```
A copy of the original paragraph
```
## Expected Description
```
A modified paragraph
```
## Attachments
Attach screenshot or files if necessary.
---
<!-- 中文版本 -->
## 实际描述
- 文件路径:例如,book/zh-cn/02-usability.md
- 原文段落:
```
复制原文段落
```
## 预期描述
```
修改后的段落
```
## 附图
必要时,请附上相关截图
================================================
FILE: .github/ISSUE_TEMPLATE/Feature_request.md
================================================
---
name: Feature request
about: 内容建议
---
<!-- English Version -->
## Motivation
Please briefly describe your motivation.
## Requirements
Please list all of your suggestions.
---
<!-- 中文版 -->
## 动机
请描述你提交内容建议的动机。
## 需求说明
请描述你提交内容建议的详单,例如具体是增加哪个知识点的说明。
================================================
FILE: .github/ISSUE_TEMPLATE/Question.md
================================================
---
name: Question
about: 提交问题
---
<!-- English Version -->
## Question
Please describe your question here, and read [How-To-Ask-Questions-The-Smart-Way](http://www.catb.org/~esr/faqs/smart-questions.html) before you submit your question.
---
<!-- 中文版 -->
## 问题描述
请在此描述你的问题,提问前请参考[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md)
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
resolve #issue_id
<!-- English Version -->
## Description
Please describe the motivation of this pull request, and what problem was solved in this PR.
## Change List
- Fix typo error of XXX
- Add description regarding XXX feature
- Resolve error content of XXX
## Reference
Please add reference if necessary
---
<!-- 中文版 -->
## 说明
此处详细说明 PR 的动机是什么、解决了什么样的问题。
## 变化箱单
- 修复了 XXX 的 typo 错误
- 增加了 XXX 相关的说明
- 解决了关于 XXX 的描述性错误
## 参考文献
如果有请注明
================================================
FILE: .github/workflows/website.yml
================================================
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
================================================
FILE: .gitignore
================================================
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
.vscode
node_modules
pdf/zh-cn/*.md
website/db.json
website/public/*
website/src/modern-cpp/zh-cn/*
website/src/modern-cpp/en-us/*
website/src/modern-cpp/exercises
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.png
website/src/modern-cpp/assets/cover-2nd-en-logo.png
website/src/modern-cpp/assets/cover-2nd-logo.png
website/src/modern-cpp/assets/figures/*
website/src/modern-cpp/assets/wechat.jpg
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at hi at changkun dot us. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# How to contribute
## Submit Issue
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.
- 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 propogation of wrong knowledge.
- 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 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
"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.
- 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 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`.
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 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 your changes also involve the exercises, you also need to synchronize the contents of the `exercises` folder.
# 如何参与贡献
## 提交 Issue
『C++ 11/14/17/20』的 issue 用于追踪书中存在的原则性的描述错误、存在的 `typo` 错误,以及向本书作者提问等。
- 通常情况下,你可能会发现书中某个段落存在错别字、语义错误、文法错误等。
这都是 `typo` 错误。如果该错误已经对你的阅读造成了一定障碍,
你也强烈的认为该 `typo` 也会影响到其他人的阅读,
那么非常欢迎[提交 issue](https://github.com/changkun/modern-cpp-tutorial/issues)
来报告 `typo` 错误。
- 对于书中存在的原则性错误,例如对源码进行分析的内容产生明显的错误、
且内容对其他人会产生严重的误导,请不要犹豫,
立即[提交 issue](https://github.com/changkun/modern-cpp-tutorial/issues) 来报告此错误,以免继续传播错误的知识。
如果可以,也请附上相关改进说明。通常情况下,如果存在这类问题,我们鼓励你一并提交改进 PR。
- 如果你在阅读本书的时候发现有部分内容难于理解,也欢迎[提交 issue](https://github.com/changkun/modern-cpp-tutorial/issues) 来询问作者表达你的疑惑。
作者会根据实际情况重新优化这一部分的内容,进而帮助他人更易阅读这部分的内容。
- 我们也欢迎你提交针对本书内容的相关建议,具体来说如果你认为书中未涉及的某个模块或者文件的源码值得讨论,也欢迎 [提交 issue](https://github.com/changkun/go-under-the-hood/issues) 来进一步讨论。
## 提交 Pull request
『C++ 11/14/17/20』是一本开源书籍,任何人都可以参与贡献自己 PR。但在提交 PR 之前请仔细阅读下面的说明:
- 当你认为需要提交一个 PR 时,请确保 [issue 列表](https://github.com/changkun/modern-cpp-tutorial/issues)中,已经包含了你想要解决的问题。
如果没有,请参考**提交 Issue** 一节中的描述,提交你的 issue,再提交你的 PR。
- 当你准备提交一个 typo 错误的 PR 时,请确保你的 PR 改进了 **超过 50 个汉字(或英文单词)** 的 `typo` 错误,否则请不要提交 PR。
- 对于一个修复原则性错误的 PR,请不要犹豫,笔者对此表示非常感谢!
- 如果非常喜欢本书,以至于希望参与本书的合著,成为作者,请发邮件询问:`hi at changkun dot us`。
本仓库提供了多种阅读方式,如果你提交一个 Pull request,则请确保你检查的如下的 checklist:
- [ ] 只改动原书正文 `book` 下的部分内容,不涉及代码片段的修改,则无需进行修改
- [ ] 如果同时还改动了正文中的代码片段,则需要同步 `code` 文件夹下对应的代码片段
- [ ] 如果改动还涉及习题的设计,则需要同步 `exercises` 文件夹下的内容
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2016 - 2020 Changkun Ou <hi@changkun.de>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
NAME=modern-cpp-tutorial
DOCKER_ENV=changkun/$(NAME):build-env
TARGET = pdf epub
LANGS = zh-cn en-us
ALL_BUILDS = website $(TARGET)
# dep
all: $(ALL_BUILDS)
$(TARGET): $(LANGS)
mkdir -p website/public/modern-cpp/$@/
for lang in $^ ; do \
cd $@/$${lang} && make && make clean && cd ../..; \
mv $@/$${lang}/$(NAME).$@ website/public/modern-cpp/$@/$(NAME)-$${lang}.$@; \
done
website:
cd website && make
build:
docker run --rm -v `pwd`:/$(NAME) $(DOCKER_ENV) make
# dev
build-env:
docker build -t $(DOCKER_ENV) -f ./docker/Dockerfile .
serve:
cd website && make s
clean:
cd pdf/zh-cn && make clean
cd pdf/en-us && make clean
cd website && make clean
docker images -f "dangling=true" -q | xargs docker rmi -f
docker image prune -f
.PHONY : $(LANGS) $(ALL_BUILDS) serve build-env build-all clean
================================================
FILE: README-zh-cn.md
================================================
<img src="assets/cover-2nd.png" alt="logo" height="550" align="right" />
# 现代 C++ 教程:高速上手 C++11/14/17/20
 [](./README.md) [](./README-zh-cn.md) [](./assets/donate.md)
## 本书目的
本书号称『高速上手』,从内容上对二十一世纪二十年代之前产生 C++ 的相关特性做了非常相对全面的介绍,读者可以自行根据下面的目录选取感兴趣的内容进行学习,快速熟悉需要了解的内容。这些特性并不需要全部掌握,只需针对自己的使用需求和特定的应用场景,学习、查阅最适合自己的新特性即可。
同时,本书在介绍这些特性的过程中,尽可能简单明了的介绍了这些特性产生的历史背景和技术需求,这为理解这些特性、运用这些特性提供了很大的帮助。
此外,笔者希望读者在阅读本书后,能够努力在新项目中直接使用现代 C++,并努力将旧项目逐步迁移到现代 C++。也算是笔者为推进现代 C++ 的普及贡献了一些绵薄之力。
## 目标读者
1. 本书假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++进行编码的人、渴望在短时间内迅速了解**现代 C++** 特性的人非常适合阅读本书;
2. 本书一定程度上介绍了一些现代 C++ 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习现代 C++ 的读者,本书的定位系**现代 C++ 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 **现代 C++** 的熟悉度。
## 开始阅读
你可以选择以下几种阅读方式:
1. [GitHub 在线](./book/zh-cn/toc.md)
2. [PDF 文档](https://changkun.de/modern-cpp/pdf/modern-cpp-tutorial-zh-cn.pdf)
3. [EPUB 文档](https://changkun.de/modern-cpp/epub/modern-cpp-tutorial-zh-cn.epub)
4. [网站](https://changkun.de/modern-cpp/)
## 相关代码
本书每章中都出现了大量的代码,如果你在跟随本书介绍特性的思路编写自己的代码遇到问题时,不妨读一读随书附上的源码,你可以在[这里](./code)中找到书中介绍过的全部的源码,所有代码按章节组织,文件夹名称为章节序号。
## 随书习题
本书每章最后还加入了少量难度极小的习题,仅用于检验你是否能混合运用当前章节中的知识点。你可以在[这里](./exercises)找到习题的答案,文件夹名称为章节序号。
## 本书网站
本书的[网站](https://changkun.de/modern-cpp)源码可以在[这里](./website)找到,由 [hexo](https://hexo.io) 和 [vuejs](https://vuejs.org) 协同构建而成。网站旨在提供一个除 GitHub 之外的阅读方式,除了在桌面端访问之外,你也可以在移动端上阅读本书。
## 构建
如果你希望在本地编译整个仓库,我们建议使用 [Docker](https://docs.docker.com/install/)。如果 Docker 在你的本地能正常使用,则可以简单的通过运行下面的指令完成构建:
```bash
$ 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)。
<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="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 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](./LICENSE)。
================================================
FILE: README.md
================================================
<img src="assets/cover-2nd-en.png" alt="logo" height="550" align="right" />
# Modern C++ Tutorial: C++11/14/17/20 On the Fly
 [](./README.md) [](./README-zh-cn.md) [](./assets/donate.md)
## Purpose
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 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 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 readers to use modern C++ directly in their new projects and migrate their old projects to modern C++ gradually after reading the book.
## Targets
- 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, 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
You can choose from the following reading methods:
- [GitHub Online](./book/en-us/toc.md)
- [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)
## Code
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
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
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
If you are interested in building everything locally, it is recommended using [Docker](https://docs.docker.com/install/). To build, simply run:
```bash
$ make build
```
## 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>
## Licenses
<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).
================================================
FILE: assets/donate.md
================================================
---
title: 资助
type: about
order: 1
---
## Donate
I would love if you support me to make the book better:
[](https://www.paypal.me/changkunde/4.99eur)
## 资助
如果你认为本书对你起到了帮助,并希望赞助作者,可以通过下面的二维码给予支持:
|微信|支付宝|
|:--:|:--:|
| | |
================================================
FILE: book/en-us/00-preface.md
================================================
---
title: Preface
type: book-en-us
order: 0
---
# Preface
[TOC]
## Introduction
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 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 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++.
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.
## 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 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 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
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 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 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 come out.
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
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
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](../../exercise). The folder name is the chapter number.
[Table of Content](./toc.md) | [Next Chapter: Towards Modern C++](./01-intro.md)
## Licenses
<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).
================================================
FILE: book/en-us/01-intro.md
================================================
---
title: "Chapter 01: Towards Modern C++"
type: book-en-us
order: 1
---
# Chapter 01: Towards Modern C++
[TOC]
**Compilation Environment**: This book will use `clang++` as the only compiler used,
and always use the `-std=c++2a` compilation flag in your code.
```bash
> clang++ -v
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
```
## 1.1 Deprecated Features
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 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`.**
```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`.**
- **`auto_ptr` is deprecated and `unique_ptr` should be used.**
- **`register` keyword is deprecated and can be used but no longer has any practical meaning.**
- **The `++` operation of the `bool` type is deprecated.**
- **If a class has a destructor, the properties for which it generates copy constructors and copy assignment operators are deprecated.**
- **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>` etc.**
- ... and many more
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
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-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
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
int add(int x, int y);
#ifdef __cplusplus
}
#endif
// foo.c
int add(int x, int y) {
return x+y;
}
// 1.1.cpp
#include "foo.h"
#include <iostream>
#include <functional>
int main() {
[out = std::ref(std::cout << "Result from C code: " << add(1, 2))](){
out.get() << ".\n";
}();
return 0;
}
```
You should first compile the C code with `gcc`:
```bash
gcc -c foo.c
```
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
clang++ 1.1.cpp foo.o -std=c++2a -o 1.1
```
Of course, you can use `Makefile` to compile the above code:
```makefile
C = gcc
CXX = clang++
SOURCE_C = foo.c
OBJECTS_C = foo.o
SOURCE_CXX = 1.1.cpp
TARGET = 1.1
LDFLAGS_COMMON = -std=c++2a
all:
$(C) -c $(SOURCE_C)
$(CXX) $(SOURCE_CXX) $(OBJECTS_C) $(LDFLAGS_COMMON) -o $(TARGET)
clean:
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` 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 are new to modern C++, you probably still don't understand the following small piece of code above, namely:
```cpp
[out = std::ref(std::cout << "Result from C code: " << add(1, 2))](){
out.get() << ".\n";
}();
```
Don't worry at the moment, we will come to meet them in our later chapters.
[Table of Content](./toc.md) | [Previous Chapter](./00-preface.md) | [Next Chapter: Language Usability Enhancements](./02-usability.md)
## 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)
[History of C++](http://en.cppreference.com/w/cpp/language/history)
- [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)
## Licenses
<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).
================================================
FILE: book/en-us/02-usability.md
================================================
---
title: "Chapter 02: Language Usability Enhancements"
type: book-en-us
order: 2
---
# Chapter 02: Language Usability Enhancements
[TOC]
When we declare, define a variable or constant, and control the flow of code,
object-oriented functions, template programming, etc., before the runtime,
it may happen when writing code or compiler compiling code.
To this end, we usually talk about **language usability**,
which refers to the language behavior that occurred before the runtime.
## 2.1 Constants
### nullptr
The purpose of `nullptr` appears to replace `NULL`. There are **null pointer constants** in the C and C++ languages,
which can be implicitly converted to null pointer value of any pointer type,
or null member pointer value of any pointer-to-member type in C++.
`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, and thus `((void*)0)` is not a valid implementation
of `NULL`. If the standard library tries to define `NULL` as `((void*)0)`, then compilation error would occur in the following code:
```cpp
char *ch = NULL;
```
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.
Consider the following two `foo` functions:
```cpp
void foo(char*);
void foo(int);
```
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.
You can try to compile the following code using clang++:
```cpp
#include <iostream>
#include <type_traits>
void foo(char *);
void foo(int);
int main() {
if (std::is_same<decltype(NULL), decltype(0)>::value)
std::cout << "NULL == 0" << std::endl;
if (std::is_same<decltype(NULL), decltype((void*)0)>::value)
std::cout << "NULL == (void *)0" << std::endl;
if (std::is_same<decltype(NULL), std::nullptr_t>::value)
std::cout << "NULL == nullptr" << std::endl;
foo(0); // will call foo(int)
// foo(NULL); // doesn't compile
foo(nullptr); // will call foo(char*)
return 0;
}
void foo(char *) {
std::cout << "foo(char*) is called" << std::endl;
}
void foo(int i) {
std::cout << "foo(int) is called" << std::endl;
}
```
The outputs are:
```bash
foo(int) is called
foo(char*) is called
```
From the output we can see that `NULL` is different from `0` and `nullptr`.
So, develop the habit of using `nullptr` directly.
In addition, in the above code, we used `decltype` and
`std::is_same` which are modern C++ syntax.
In simple terms, `decltype` is used for type derivation,
and `std::is_same` is used to compare the equality of the two types.
We will discuss them in detail later in the [decltype](#decltype) section.
### constexpr
C++ itself already has the concept of constant expressions, such as 1+2,
3\*4. Such expressions always produce the same result without any side effects.
If the compiler can directly optimize and embed these expressions into the program at
compile-time, it will increase the performance of the program. A very obvious example
is in the definition phase of an array:
```cpp
#include <iostream>
#define LEN 10
int len_foo() {
int i = 2;
return i;
}
constexpr int len_foo_constexpr() {
return 5;
}
constexpr int fibonacci(const int n) {
return n == 1 || n == 2 ? 1 : fibonacci(n-1) + fibonacci(n-2);
}
int main() {
char arr_1[10]; // legal
char arr_2[LEN]; // legal
int len = 10;
// char arr_3[len]; // illegal
const int len_2 = len + 1;
constexpr int len_2_constexpr = 1 + 2 + 3;
// char arr_4[len_2]; // illegal, but ok for most of the compilers
char arr_4[len_2_constexpr]; // legal
// char arr_5[len_foo()+5]; // illegal
char arr_6[len_foo_constexpr() + 1]; // legal
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
std::cout << fibonacci(10) << std::endl;
return 0;
}
```
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?
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,
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,
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.
> Note that most compilers now have their compiler optimizations.
> 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.
C++11 provides `constexpr` to let the user explicitly declare that the function or
object constructor will become a constant expression at compile time.
This keyword explicitly tells the compiler that it should verify that `len_foo`
should be a compile-time constant expression.
In addition, the function of `constexpr` can use recursion:
```cpp
constexpr int fibonacci(const int n) {
return n == 1 || n == 2 ? 1 : fibonacci(n-1) + fibonacci(n-2);
}
```
Starting with C++14,
the constexpr function can use simple statements such as local variables,
loops, and branches internally.
For example, the following code cannot be compiled under the C++11 standard:
```cpp
constexpr int fibonacci(const int n) {
if(n == 1) return 1;
if(n == 2) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
```
To do this, we can write a simplified version like this
to make the function available from C++11:
```cpp
constexpr int fibonacci(const int n) {
return n == 1 || n == 2 ? 1 : fibonacci(n-1) + fibonacci(n-2);
}
```
## 2.2 Variables and initialization
### if-switch
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,
but there is always no way to declare a temporary variable in the `if` and `switch` statements.
E.g:
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4};
// since c++17, can be simplified by using `auto`
const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 2);
if (itr != vec.end()) {
*itr = 3;
}
if (const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 3);
itr != vec.end()) {
*itr = 4;
}
// should output: 1, 4, 3, 4. can be simplified using `auto`
for (std::vector<int>::iterator element = vec.begin(); element != vec.end();
++element)
std::cout << *element << std::endl;
}
```
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 a variable need to traverse
the entire `std::vector` again. C++17 eliminates this limitation so that
we can do this in if(or switch):
```cpp
if (const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 3);
itr != vec.end()) {
*itr = 4;
}
```
Is it similar to the Go?
### Initializer list
Initialization is a very important language feature,
the most common one is when the object is initialized.
In traditional C++, different objects have different initialization methods,
such as ordinary arrays, PODs (**P**lain **O**ld **D**ata,
i.e. classes without constructs, destructors, and virtual functions)
Or struct type can be initialized with `{}`,
which is what we call the initialization list.
For the initialization of the class object,
you need to use the copy construct,
or you need to use `()`.
These different methods are specific to each other and cannot be generic.
E.g:
```cpp
#include <iostream>
#include <vector>
class Foo {
public:
int value_a;
int value_b;
Foo(int a, int b) : value_a(a), value_b(b) {}
};
int main() {
// before C++11
int arr[3] = {1, 2, 3};
Foo foo(1, 2);
std::vector<int> vec = {1, 2, 3, 4, 5};
std::cout << "arr[0]: " << arr[0] << std::endl;
std::cout << "foo:" << foo.value_a << ", " << foo.value_b << std::endl;
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << std::endl;
}
return 0;
}
```
To solve this problem,
C++11 first binds the concept of the initialization list to the type
and calls it `std::initializer_list`,
allowing the constructor or other function to use the initialization list
like a parameter, which is the initialization of class objects provides
a unified bridge between normal arrays and POD initialization methods,
such as:
```cpp
#include <initializer_list>
#include <vector>
#include <iostream>
class MagicFoo {
public:
std::vector<int> vec;
MagicFoo(std::initializer_list<int> list) {
for (std::initializer_list<int>::iterator it = list.begin();
it != list.end(); ++it)
vec.push_back(*it);
}
};
int main() {
// after C++11
MagicFoo magicFoo = {1, 2, 3, 4, 5};
std::cout << "magicFoo: ";
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 will be specially taken care of during initialization.
In addition to the object construction, the initialization list can also
be used as a formal parameter of a normal function, for example:
```Cpp
public:
void foo(std::initializer_list<int> list) {
for (std::initializer_list<int>::iterator it = list.begin();
it != list.end(); ++it) vec.push_back(*it);
}
magicFoo.foo({6,7,8,9});
```
Second, C++11 also provides a uniform syntax for initializing arbitrary objects, such as:
```cpp
Foo foo2 {3, 4};
```
### Structured binding
Structured bindings provide functionality similar to the multiple return values
provided in other languages. In the chapter on containers,
we will learn that C++11 has added a `std::tuple` container for
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
the elements in the tuple from the tuple,
although we can unpack the tuple using `std::tie`
But we still have to be very clear about how many objects this tuple contains,
what type of each object is, very troublesome.
C++17 completes this setting,
and the structured bindings let us write code like this:
```cpp
#include <iostream>
#include <tuple>
std::tuple<int, double, std::string> f() {
return std::make_tuple(1, 2.3, "456");
}
int main() {
auto [x, y, z] = f();
std::cout << x << ", " << y << ", " << z << std::endl;
return 0;
}
```
The `auto` type derivation is described in the
[auto type inference](#auto) section.
## 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 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.
### auto
`auto` has been in C++ for a long time, but it always exists as an indicator of a storage type, coexisting with `register`. In traditional C++, if a variable is not declared as a `register` variable, it is automatically treated as an `auto` variable. And with `register` being deprecated (used as a reserved keyword in C++17 and later used, it doesn't currently make sense), the semantic change to `auto` is very natural.
One of the most common and notable examples of type derivation using `auto` is the iterator. You should see the lengthy iterative writing in traditional C++ in the previous section:
```cpp
// before C++11
// cbegin() returns vector<int>::const_iterator
// and therefore it is type vector<int>::const_iterator
for(vector<int>::const_iterator it = vec.cbegin(); it != vec.cend(); ++it)
```
When we have `auto`:
```cpp
#include <initializer_list>
#include <vector>
#include <iostream>
class MagicFoo {
public:
std::vector<int> vec;
MagicFoo(std::initializer_list<int> list) {
for (auto it = list.begin(); it != list.end(); ++it) {
vec.push_back(*it);
}
}
};
int main() {
MagicFoo magicFoo = {1, 2, 3, 4, 5};
std::cout << "magicFoo: ";
for (auto it = magicFoo.vec.begin(); it != magicFoo.vec.end(); ++it) {
std::cout << *it << ", ";
}
std::cout << std::endl;
return 0;
}
```
Some other common usages:
```cpp
auto i = 5; // i as int
auto arr = new auto(10); // arr as int *
```
Since C++ 14, `auto` can even be used as function arguments in generic lambda expressions,
and such functionality is generalized to normal functions in C++ 20.
Consider the following example:
```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:
>
> ```cpp
> 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'
> auto auto_arr2[10] = {arr};
> ```
### decltype
The `decltype` keyword is used to solve the defect that the auto keyword
can only type the variable. Its usage is very similar to `typeof`:
```cpp
decltype(expression)
```
Sometimes we may need to calculate the type of an expression, for example:
```cpp
auto x = 1;
auto y = 2;
decltype(x+y) z;
```
You have seen in the previous example that
`decltype` is used to infer the usage of the type.
The following example is to determine
if the above variables `x, y, z` are of the same type:
```cpp
if (std::is_same<decltype(x), int>::value)
std::cout << "type x == int" << std::endl;
if (std::is_same<decltype(x), float>::value)
std::cout << "type x == float" << std::endl;
if (std::is_same<decltype(x), decltype(z)>::value)
std::cout << "type z == type x" << std::endl;
```
Among them, `std::is_same<T, U>` is used to determine whether
the two types `T` and `U` are equal. The output is:
```
type x == int
type z == type x
```
### tail type inference
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
template<typename R, typename T, typename U>
R add(T x, U 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](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 very ugly because the programmer must explicitly
indicate the return type when using this template function.
But in fact, we don't know what kind of operation
the `add()` function will do, and what kind of return type to get.
This problem was solved in C++11. Although you may immediately
react to using `decltype` to derive the type of `x+y`,
write something like this:
```cpp
decltype(x+y) add(T x, U y)
```
But in fact, this way of writing can not be compiled.
This is because `x` and `y` have not been defined
when the compiler reads decltype(x+y).
To solve this problem, C++11 also introduces a trailing return type,
which uses the auto keyword to post the return type:
```cpp
template<typename T, typename U>
auto add2(T x, U y) -> decltype(x+y){
return x + y;
}
```
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:
```cpp
template<typename T, typename U>
auto add3(T x, U y){
return x + y;
}
```
You can check if the type derivation is correct:
```cpp
// after c++11
auto w = add2<int, double>(1, 2.0);
if (std::is_same<decltype(w), double>::value) {
std::cout << "w is double: ";
}
std::cout << w << std::endl;
// after c++14
auto q = add3<double, int>(1.0, 2);
std::cout << "q: " << q << std::endl;
```
### decltype(auto)
`decltype(auto)` is a slightly more complicated use of C++14.
> To understand it you need to know the concept of parameter forwarding
> in C++, which we will cover in detail in the
> [Language Runtime Enhancements](./03-runtime.md) chapter,
> and you can come back to the contents of this section later.
In simple terms, `decltype(auto)` is mainly used to derive
the return type of a forwarding function or package,
which does not require us to explicitly specify
the parameter expression of `decltype`.
Consider the following example, when we need to wrap the following
two functions:
```cpp
std::string lookup1();
std::string& lookup2();
```
In C++11:
```cpp
std::string look_up_a_string_1() {
return lookup1();
}
std::string& look_up_a_string_2() {
return lookup2();
}
```
With `decltype(auto)`, we can let the compiler do this annoying parameter forwarding:
```cpp
decltype(auto) look_up_a_string_1() {
return lookup1();
}
decltype(auto) look_up_a_string_2() {
return lookup2();
}
```
## 2.4 Control flow
### 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:
```cpp
#include <iostream>
template<typename T>
auto print_type_info(const T& t) {
if constexpr (std::is_integral<T>::value) {
return t + 1;
} else {
return t + 0.001;
}
}
int main() {
std::cout << print_type_info(5) << std::endl;
std::cout << print_type_info(3.14) << std::endl;
}
```
At compile time, the actual code will behave as follows:
```cpp
int print_type_info(const int& t) {
return t + 1;
}
double print_type_info(const double& t) {
return t + 0.001;
}
int main() {
std::cout << print_type_info(5) << std::endl;
std::cout << print_type_info(3.14) << std::endl;
}
```
### Range-based for loop
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:
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4};
if (auto itr = std::find(vec.begin(), vec.end(), 3); itr != vec.end()) *itr = 4;
for (auto element : vec)
std::cout << element << std::endl; // read only
for (auto &element : vec) {
element += 1; // writeable
}
for (auto element : vec)
std::cout << element << std::endl; // read only
}
```
## 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, 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
In traditional C++, templates are instantiated by the compiler only when they are used. In other words, as long as a fully defined template is encountered in the code compiled in each compilation unit (file), it will be instantiated. This results in an increase in compile time due to repeated instantiations. Also, we have no way to tell the compiler not to trigger the instantiation of the template.
To this end, C++11 introduces an external template that extends the syntax of the original mandatory compiler to instantiate a template at a specific location, allowing us to explicitly tell the compiler when to instantiate the template:
```cpp
template class std::vector<bool>; // force instantiation
extern template class std::vector<double>; // should not instantiation in current file
```
### The ">"
In the traditional C++ compiler, `>>` is always treated as a right shift operator. But actually we can easily write the code for the nested template:
```cpp
std::vector<std::vector<int>> matrix;
```
This is not compiled under the traditional C++ compiler,
and C++11 starts with continuous right angle brackets that become legal
and can be compiled successfully.
Even the following writing can be compiled by:
```cpp
template<bool T>
class MagicType {
bool magic = T;
};
// in main function:
std::vector<MagicType<(1>2)>> magic; // legal, but not recommended
```
### Type alias templates
Before you understand the type alias template, you need to understand the difference between "template" and "type". Carefully understand this sentence: **Templates are used to generate types.** In traditional C++, `typedef` can define a new name for the type, but there is no way to define a new name for the template. Because the template is not a type. E.g:
```cpp
template<typename T, typename U>
class MagicType {
public:
T dark;
U magic;
};
// not allowed
template<typename T>
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`:
> 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
typedef int (*process)(void *);
using NewProcess = int(*)(void *);
template<typename T>
using TrueDarkMagic = MagicType<std::vector<T>, std::string>;
int main() {
TrueDarkMagic<bool> you;
}
```
### Variadic templates
The template has always been one of C++'s unique **Black Magic**.
In traditional C++,
both a class template and a function template could only accept
a fixed set of template parameters as specified;
C++11 added a new representation, allowing any number,
template parameters of any category,
and there is no need to fix the number of parameters when defining.
```cpp
template<typename... Ts> class Magic;
```
The template class Magic object can accept an unrestricted number of typename as
a formal parameter of the template, such as the following definition:
```cpp
class Magic<int,
std::vector<int>,
std::map<std::string,
std::vector<int>>> darkMagic;
```
Since it is arbitrary, a template parameter with a number of 0 is also possible: `class Magic<> nothing;`.
If you do not want to generate 0 template parameters, you can manually define at least one template parameter:
```cpp
template<typename Require, typename... Args> class Magic;
```
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:
```cpp
template<typename... Args> void printf(const std::string &str, Args... args);
```
Then we define variable length template parameters,
how to unpack the parameters?
First, we can use `sizeof...` to calculate the number of arguments:
```cpp
#include <iostream>
template<typename... Ts>
void magic(Ts... args) {
std::cout << sizeof...(args) << std::endl;
}
```
We can pass any number of arguments to the `magic` function:
```cpp
magic(); // 0
magic(1); // 1
magic(1, ""); // 2
```
Second, the parameters are unpacked. So far there is no simple way to process
the parameter package, but there are two classic processing methods:
**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:
```cpp
#include <iostream>
template<typename T0>
void printf1(T0 value) {
std::cout << value << std::endl;
}
template<typename T, typename... Ts>
void printf1(T value, Ts... args) {
std::cout << value << std::endl;
printf1(args...);
}
int main() {
printf1(1, 2, "123", 1.1);
return 0;
}
```
**2. Variable parameter template expansion**
You should feel that this is very cumbersome. Added support for variable parameter template expansion in C++17, so you can write `printf` in a function:
```cpp
template<typename T0, typename... T>
void printf2(T0 t0, T... t) {
std::cout << t0 << std::endl;
if constexpr (sizeof...(t) > 0) printf2(t...);
}
```
> In fact, sometimes we use variable parameter templates, but we don't necessarily need to traverse the parameters one by one. We can use the features of `std::bind` and perfect forwarding to achieve the binding of functions and parameters, thus achieving success. The purpose of the call.
**3. Initialize list expansion**
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:
```cpp
template<typename T, typename... Ts>
auto printf3(T value, Ts... args) {
std::cout << value << std::endl;
(void) std::initializer_list<T>{([&args] {
std::cout << args << std::endl;
}(), value)...};
}
```
In this code, the initialization list provided in C++11 and the properties of the Lambda expression (mentioned in the next section) are additionally used.
By initializing the list, `(lambda expression, value)...` will be expanded. Due to the appearance of the comma expression, the previous lambda expression is executed first, and the output of the parameter is completed.
To avoid compiler warnings, we can explicitly convert `std::initializer_list` to `void`.
### Fold expression
In C++ 17, this feature of the variable length parameter is further brought to the expression, consider the following example:
```cpp
#include <iostream>
template<typename ... T>
auto sum(T ... t) {
return (t + ...);
}
int main() {
std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
}
```
### Non-type template parameter deduction
What we mainly mentioned above is a form of template parameters: type template parameters.
```cpp
template <typename T, typename U>
auto add(T t, U u) {
return t+u;
}
```
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
to be template parameters, i.e. non-type template parameters:
```cpp
template <typename T, int BufSize>
class buffer_t {
public:
T& alloc();
void free(T& item);
private:
T data[BufSize];
}
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.
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,
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.
E.g:
```cpp
template <auto value> void foo() {
std::cout << value << std::endl;
return;
}
int main() {
foo<10>(); // value as int
}
```
## 2.6 Object-oriented
### Delegate 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:
```cpp
#include <iostream>
class Base {
public:
int value1;
int value2;
Base() {
value1 = 1;
}
Base(int value) : Base() { // delegate Base() constructor
value2 = value;
}
};
int main() {
Base b(2);
std::cout << b.value1 << std::endl;
std::cout << b.value2 << std::endl;
}
```
### Inheritance constructor
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
#include <iostream>
class Base {
public:
int value1;
int value2;
Base() {
value1 = 1;
}
Base(int value) : Base() { // delegate Base() constructor
value2 = value;
}
};
class Subclass : public Base {
public:
using Base::Base; // inheritance constructor
};
int main() {
Subclass s(3);
std::cout << s.value1 << std::endl;
std::cout << s.value2 << std::endl;
}
```
### Explicit virtual function overwrite
In traditional C++, it is often prone to accidentally overloading virtual functions. E.g:
```cpp
struct Base {
virtual void foo();
};
struct SubClass: Base {
void foo();
};
```
`SubClass::foo` may not be a programmer trying to overload a virtual function, just adding a function with the same name. Another possible scenario is that when the virtual function of the base class is deleted, the subclass owns the old function and no longer overloads the virtual function and turns it into a normal class method, which has catastrophic consequences.
C++11 introduces the two keywords `override` and `final` to prevent this from happening.
### 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 with consistent function signature, otherwise it will not compile:
```cpp
struct Base {
virtual void foo(int);
};
struct SubClass: Base {
virtual void foo(int) override; // legal
virtual void foo(float) override; // illegal, no virtual function in super class
};
```
### final
`final` is to prevent the class from being continued to inherit and to terminate
the virtual function to continue to be overloaded.
```cpp
struct Base {
virtual void foo() final;
};
struct SubClass1 final: Base {
}; // legal
struct SubClass2 : SubClass1 {
}; // illegal, SubClass1 has final
struct SubClass3: Base {
void foo(); // illegal, foo has final
};
```
### 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. 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.
Also, the default constructor generated by the compiler cannot exist at the same time as the user-defined constructor. If the user defines any constructor, the compiler will no longer generate the default constructor, but sometimes we want to have both constructors at the same time, which is awkward.
C++11 provides a solution to the above requirements, allowing explicit declarations to take or reject functions that come with the compiler. E.g:
```cpp
class Magic {
public:
Magic() = default; // explicit let compiler use default constructor
Magic& operator=(const Magic&) = delete; // explicit declare refuse constructor
Magic(int magic_number);
}
```
### 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.
C++11 introduces an enumeration class and declares it using the syntax of `enum class`:
```cpp
enum class new_enum : unsigned int {
value1,
value2,
value3 = 100,
value4 = 100
};
```
The enumeration thus defined implements type safety. First, it cannot be implicitly converted to an integer, nor can it be compared to integer numbers, and it is even less likely to compare enumerated values of different enumerated types. But if the values specified are the same between the same enumerated values, then you can compare:
```cpp
if (new_enum::value3 == new_enum::value4) { // true
std::cout << "new_enum::value3 == new_enum::value4" << std::endl;
}
```
In this syntax, the enumeration type is followed by a colon and a type keyword to specify the type of the enumeration value in the enumeration, which allows us to assign a value to the enumeration (int is used by default when not specified).
And we want to get the value of the enumeration value, we will have to explicitly type conversion, but we can overload the `<<` operator to output, you can collect the following code snippet:
```cpp
#include <iostream>
template<typename T>
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);
}
```
At this point, the following code will be able to be compiled:
```cpp
std::cout << new_enum::value3 << std::endl
```
## Conclusion
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
2. Scope for iteration
3. Initialization list
4. Variable parameter template
## Exercises
1. Using structured binding, implement the following functions with just one line of function code:
```cpp
#include <string>
#include <map>
#include <iostream>
template <typename Key, typename Value, typename F>
void update(std::map<Key, Value>& m, F foo) {
// TODO:
}
int main() {
std::map<std::string, long long int> m {
{"a", 1},
{"b", 2},
{"c", 3}
};
update(m, [](std::string key) {
return std::hash<std::string>{}(key);
});
for (auto&& [key, value] : m)
std::cout << key << ":" << value << std::endl;
}
```
2. Try to implement a function for calculating the mean with [Fold Expression](#Fold-expression), allowing any arguments to be passed in.
> Refer to the answer [see this](../../exercises/2).
[Table of Content](./toc.md) | [Previous Chapter](./01-intro.md) | [Next Chapter: Language Runtime Enhancements](./03-runtime.md)
## Licenses
<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).
================================================
FILE: book/en-us/03-runtime.md
================================================
---
title: "Chapter 03: Language Runtime Enhancements"
type: book-en-us
order: 3
---
# Chapter 03: Language Runtime Enhancements
[TOC]
## 3.1 Lambda Expression
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 name to call a function. There are many, many scenes like this.
So anonymous functions are almost standard in modern programming languages.
### Basics
The basic syntax of a Lambda expression is as follows:
```
[capture list] (parameter list) mutable(optional) exception attribute -> return type {
// function body
}
```
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.
The return value is in the form of a `->`
(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 internal function body of a lambda expression cannot use variables outside
the body of the function by default.
At this time, the capture list can serve to transfer external data.
According to the behavior passed,
the capture list is also divided into the following types:
#### 1. Value capture
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
when the lambda expression is created, not when it is called:
```cpp
void lambda_value_capture() {
int value = 1;
auto copy_value = [value] {
return value;
};
value = 100;
auto stored_value = copy_value();
std::cout << "stored_value = " << stored_value << std::endl;
// At this moment, stored_value == 1, and value == 100.
// Because copy_value has copied when its was created.
}
```
#### 2. Reference capture
Similar to a reference pass, the reference capture saves the reference and the value changes.
```cpp
void lambda_reference_capture() {
int value = 1;
auto copy_value = [&value] {
return value;
};
value = 100;
auto stored_value = copy_value();
std::cout << "stored_value = " << stored_value << std::endl;
// At this moment, stored_value == 100, value == 100.
// Because copy_value stores reference
}
```
#### 3. Implicit capture
Manually writing a capture list is sometimes very complicated.
This mechanical work can be handled by the compiler.
At this point, you can write a `&` or `=` to the compiler to
declare the reference or value capture.
To summarize, capture provides the ability for lambda expressions
to use external values. The four most common forms of
capture lists can be:
- \[\] empty capture list
- \[name1, name2, ...\] captures a series of variables
- \[&\] reference capture, determine the reference capture list from the uses the in function body
- \[=\] value capture, determine the value capture list from the uses in the function body
#### 4. Expression capture
> This section needs to understand the rvalue references and smart pointers that
> will be mentioned later.
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
and not capture the rvalue.
C++14 gives us the convenience of allowing the captured members to be initialized
with arbitrary expressions, which allows the capture of rvalues.
The type of the captured variable being declared is judged according to the expression,
and the judgment is the same as using `auto`:
```cpp
#include <iostream>
#include <memory> // std::make_unique
#include <utility> // std::move
void lambda_expression_capture() {
auto important = std::make_unique<int>(1);
auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int {
return x+y+v1+(*v2);
};
std::cout << add(3,4) << std::endl;
}
```
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
initialize it in the expression.
### Generic Lambda
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.
But lambda expressions are not regular functions, without further specification on the typed parameter list, lambda expressions cannot utilize templates. 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 utilize template generics:
```cpp
void lambda_generic() {
auto generic = [](auto x, auto y) {
return x+y;
};
std::cout << generic(1, 2) << std::endl;
std::cout << generic(1.1, 2.2) << std::endl;
}
```
## 3.2 Function Object Wrapper
Although the features are part of the standard library and not found in runtime,
it enhances the runtime capabilities of the C++ language.
This part of the content is also very important, so put it here for the introduction.
### `std::function`
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).
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:
```cpp
#include <iostream>
using foo = void(int); // function pointer
void functional(foo f) {
f(1);
}
int main() {
auto f = [](int value) {
std::cout << value << std::endl;
};
functional(f); // call by function pointer
f(1); // call by lambda expression
return 0;
}
```
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.
In C++11, these concepts are unified.
The type of object that can be called is collectively called the callable type.
This type is introduced by `std::function`.
C++11 `std::function` is a generic, polymorphic function wrapper
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,
the call to a function pointer is not type-safe), in other words,
a container of functions. When we have a container for functions,
we can more easily handle functions and function pointers as objects. e.g:
```cpp
#include <functional>
#include <iostream>
int foo(int para) {
return para;
}
int main() {
// std::function wraps a function that take int paremeter and returns int value
std::function<int(int)> func = foo;
int important = 10;
std::function<int(int)> func2 = [&](int value) -> int {
return 1+value+important;
};
std::cout << func(10) << std::endl;
std::cout << func2(10) << std::endl;
}
```
### `std::bind` and `std::placeholder`
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
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,
and then complete the call after the parameters are complete. e.g:
```cpp
int foo(int a, int b, int c) {
;
}
int main() {
// bind parameter 1, 2 on function foo,
// and use std::placeholders::_1 as placeholder for the first parameter.
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
// when call bindFoo, we only need one param left
bindFoo(1);
}
```
> **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`.
## 3.3 rvalue Reference
rvalue references are one of the important features introduced by C++11
that are synonymous with Lambda expressions. Its introduction solves
a large number of historical issues in C++.
Eliminating extra overhead such as `std::vector`, `std::string`,
and making the function object container `std::function` possible.
### lvalue, rvalue, prvalue, xvalue
To understand what the rvalue reference is all about, you must have a clear
understanding of the lvalue and the rvalue.
**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
an expression (not necessarily an assignment expression).
**Rvalue, right value**, the value on the right refers to the temporary object
that no longer exists after the expression ends.
In C++11, in order to introduce powerful rvalue references,
the concept of rvalue values is further divided into:
prvalue, and xvalue.
**pvalue, pure rvalue**, purely rvalue, either purely literal,
such as `10`, `true`; either the result of the evaluation is equivalent to
a literal or anonymous temporary object, for example `1+2`.
Temporary variables returned by non-references, temporary variables generated
by operation expressions, original literals, and Lambda expressions
are all pure rvalue values.
Note that a literal (except a string literal) is a prvalue. However, a string
literal is an lvalue with type `const char` array. Consider the following examples:
```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:
```cpp
std::vector<int> foo() {
std::vector<int> temp = {1, 2, 3, 4};
return temp;
}
std::vector<int> v = foo();
```
In such code, as far as the traditional understanding is concerned,
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 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).
In the last line, `v` is the lvalue, and the value returned by `foo()` is
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 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`
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.
### rvalue reference and lvalue reference
To get a xvalue, you need to use the declaration of the rvalue reference: `T &&`,
where `T` is the type.
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.
C++11 provides the `std::move` method to unconditionally convert
lvalue parameters to rvalues.
With it we can easily get a rvalue temporary object, for example:
```cpp
#include <iostream>
#include <string>
void reference(std::string& str) {
std::cout << "lvalue" << std::endl;
}
void reference(std::string&& str) {
std::cout << "rvalue" << std::endl;
}
int main()
{
std::string lv1 = "string,"; // lv1 is a 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::cout << rv1 << std::endl; // string,
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
std::cout << lv2 << std::endl; // string,string,
std::string&& rv2 = lv1 + lv2; // legal, rvalue ref extend lifecycle
rv2 += "string"; // legal, non-const reference can be modified
std::cout << rv2 << std::endl; // string,string,string,string
reference(rv2); // output: lvalue
return 0;
}
```
`rv2` refers to an rvalue, but since it is a reference,
`rv2` is still an lvalue.
Note that there is a very interesting historical issue here,
let's look at the following code:
```cpp
#include <iostream>
int main() {
// int &a = std::move(1); // illegal, non-const lvalue reference cannot ref rvalue
const int &b = std::move(1); // legal, const lvalue reference can
std::cout << b << std::endl;
}
```
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:
```cpp
void increase(int & v) {
v++;
}
void foo() {
double s = 1;
increase(s);
}
```
Since `int&` can't reference a parameter of type `double`,
you must generate a temporary value to hold the value of `s`.
Thus, when `increase()` modifies this temporary value,
`s` itself is not modified after the call is completed.
The second question, why do constant references allow binding to non-lvalues?
The reason is simple because Fortran needs it.
### Move semantics
Traditional C++ has designed the concept of copy/copy for class objects
through copy constructors and assignment operators,
but to implement the movement of resources,
The caller must use the method of copying and then destructing first,
otherwise, you need to implement the interface of the mobile object yourself.
Imagine moving your home directly to your new home instead of
copying everything (rebuy) to your new home.
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",
resulting in a large amount of data copying, wasting time and space.
The appearance of rvalue references solves the confusion of these two concepts,
for example:
```cpp
#include <iostream>
class A {
public:
int *pointer;
A():pointer(new int(1)) {
std::cout << "construct" << pointer << std::endl;
}
A(A& a):pointer(new int(*a.pointer)) {
std::cout << "copy" << pointer << std::endl;
} // meaningless object copy
A(A&& a):pointer(a.pointer) {
a.pointer = nullptr;
std::cout << "move" << pointer << std::endl;
}
~A(){
std::cout << "destruct" << pointer << std::endl;
delete pointer;
}
};
// avoid compiler optimization
A return_rvalue(bool test) {
A a,b;
if(test) return a; // equal to static_cast<A&&>(a);
else return b; // equal to static_cast<A&&>(b);
}
int main() {
A obj = return_rvalue(false);
std::cout << "obj:" << std::endl;
std::cout << obj.pointer << std::endl;
std::cout << *obj.pointer << std::endl;
return 0;
}
```
In the code above:
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.
This avoids meaningless copy constructs and enhances performance.
Let's take a look at an example involving a standard library:
```cpp
#include <iostream> // std::cout
#include <utility> // std::move
#include <vector> // std::vector
#include <string> // std::string
int main() {
std::string str = "Hello world.";
std::vector<std::string> v;
// use push_back(const T&), copy
v.push_back(str);
// "str: Hello world."
std::cout << "str: " << str << std::endl;
// use push_back(const T&&),
// no copy the string will be moved to vector,
// and therefore std::move can reduce copy cost
v.push_back(std::move(str));
// str is empty now
std::cout << "str: " << str << std::endl;
return 0;
}
```
### Perfect forwarding
As we mentioned earlier, the rvalue reference of a declaration is actually an lvalue.
This creates problems for us to parameterize (pass):
```cpp
#include <iostream>
#include <utility>
void reference(int& v) {
std::cout << "lvalue reference" << std::endl;
}
void reference(int&& v) {
std::cout << "rvalue reference" << std::endl;
}
template <typename T>
void pass(T&& v) {
std::cout << " normal param passing: ";
reference(v);
}
int main() {
std::cout << "rvalue pass:" << std::endl;
pass(1);
std::cout << "lvalue pass:" << std::endl;
int l = 1;
pass(l);
return 0;
}
```
For `pass(1)`, although the value is the rvalue, since `v` is a reference, it is also an 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&&)`?
This is based on the **reference collapsing rule**: In traditional C++, we are not able to continue to reference a reference type.
However,
C++ has relaxed this practice with the advent of rvalue references,
resulting in a reference collapse rule that allows us to reference references,
both lvalue and rvalue. But follow the rules below:
| Function parameter type | Argument parameter type | Post-derivation function parameter type |
| :---------------------: | :---------------------: | :-------------------------------------: |
| T& | lvalue ref | T& |
| T& | rvalue ref | T& |
| T&& | lvalue 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.
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` successful delivery of lvalues.
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).
To solve this problem, we should use `std::forward` to forward (pass) the parameters:
```cpp
#include <iostream>
#include <utility>
void reference(int& v) {
std::cout << "lvalue reference" << std::endl;
}
void reference(int&& v) {
std::cout << "rvalue reference" << std::endl;
}
template <typename T>
void pass(T&& v) {
std::cout << " normal param passing: ";
reference(v);
std::cout << " std::move param passing: ";
reference(std::move(v));
std::cout << " std::forward param passing: ";
reference(std::forward<T>(v));
std::cout << "static_cast<T&&> param passing: ";
reference(static_cast<T&&>(v));
}
int main() {
std::cout << "rvalue pass:" << std::endl;
pass(1);
std::cout << "lvalue pass:" << std::endl;
int l = 1;
pass(l);
return 0;
}
```
The outputs are:
```
rvalue pass:
normal param passing: lvalue reference
std::move param passing: rvalue reference
std::forward param passing: rvalue reference
static_cast<T&&> param passing: rvalue reference
lvalue pass:
normal param passing: lvalue reference
std::move param passing: rvalue reference
std::forward param passing: lvalue reference
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.
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.
`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<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.
Let's take a quick look at the concrete implementation of `std::forward`. `std::forward` contains two overloads:
```cpp
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp&&>(__t);
}
```
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`.
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,
`_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.
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.
## Conclusion
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
1. Function object container std::function
2. rvalue reference
[Table of Content](./toc.md) | [Previous Chapter](./02-usability.md) | [Next Chapter: Containers](./04-containers.md)
## Further Readings
- [Bjarne Stroustrup, The Design and Evolution of C++](https://www.amazon.com/Design-Evolution-C-Bjarne-Stroustrup/dp/0201543303)
## Licenses
<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).
================================================
FILE: book/en-us/04-containers.md
================================================
---
title: "Chapter 04 Containers"
type: book-en-us
order: 4
---
# Chapter 04 Containers
[TOC]
## 4.1 Linear Container
### `std::array`
When you see this container, you will have this problem:
1. Why introduce `std::array` instead of `std::vector` directly?
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.
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.
```cpp
std::vector<int> v;
std::cout << "size:" << v.size() << 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
// automatically expanded as needed.
// But if there is not enough space, you need to redistribute more memory,
// and reallocating memory is usually a performance-intensive operation.
v.push_back(1);
v.push_back(2);
v.push_back(3);
std::cout << "size:" << v.size() << std::endl; // output 3
std::cout << "capacity:" << v.capacity() << std::endl; // output 4
// The auto-expansion logic here is very similar to Golang's slice.
v.push_back(4);
v.push_back(5);
std::cout << "size:" << v.size() << std::endl; // output 5
std::cout << "capacity:" << v.capacity() << std::endl; // output 8
// As can be seen below, although the container empties the element,
// the memory of the emptied element is not returned.
v.clear();
std::cout << "size:" << v.size() << std::endl; // output 0
std::cout << "capacity:" << v.capacity() << std::endl; // output 8
// Additional memory can be returned to the system via the shrink_to_fit() call
v.shrink_to_fit();
std::cout << "size:" << v.size() << std::endl; // output 0
std::cout << "capacity:" << v.capacity() << std::endl; // output 0
```
The second problem is much simpler. Using `std::array` can make the code more "modern" and encapsulate some manipulation functions, such as getting the array size and checking if it is not empty, and also using the standard friendly. Container algorithms in the library, such as `std::sort`.
Using `std::array` is as simple as specifying its type and size:
```cpp
std::array<int, 4> arr = {1, 2, 3, 4};
arr.empty(); // check if container is empty
arr.size(); // return the size of the container
// iterator support
for (auto &i : arr)
{
// ...
}
// use lambda expression for sort
std::sort(arr.begin(), arr.end(), [](int a, int b) {
return b < a;
});
// array size must be constexpr
constexpr int len = 4;
std::array<int, len> arr = {1, 2, 3, 4};
// illegal, different than C-style array, std::array will not deduce to T*
// int *arr_p = arr;
```
When we started using `std::array`, it was inevitable that we would encounter a C-style compatible interface. There are three ways to do this:
```cpp
void foo(int *p, int len) {
return;
}
std::array<int, 4> arr = {1,2,3,4};
// C-stype parameter passing
// foo(arr, arr.size()); // illegal, cannot convert implicitly
foo(&arr[0], arr.size());
foo(arr.data(), arr.size());
// use `std::sort`
std::sort(arr.begin(), arr.end());
```
### `std::forward_list`
`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.
Provides element insertion of `O(1)` complexity, does not support fast random access (this is also a feature of linked lists),
It is also the only container in the standard library container that does not provide the `size()` method. Has a higher space utilization than `std::list` when bidirectional iteration is not required.
## 4.2 Unordered Container
We are already familiar with the ordered container `std::map`/`std::set` in traditional C++. These elements are internally implemented by red-black trees.
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.
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.
C++11 introduces two unordered containers: `std::unordered_map`/`std::unordered_multimap` and
`std::unordered_set`/`std::unordered_multiset`.
Their usage is basically similar to the original `std::map`/`std::multimap`/`std::set`/`set::multiset`
Since these containers are already familiar to us, we will not compare them one by one. Let's compare `std::map` and `std::unordered_map` directly:
```cpp
#include <iostream>
#include <string>
#include <unordered_map>
#include <map>
int main() {
// initialized in same order
std::unordered_map<int, std::string> u = {
{1, "1"},
{3, "3"},
{2, "2"}
};
std::map<int, std::string> v = {
{1, "1"},
{3, "3"},
{2, "2"}
};
// iterates in the same way
std::cout << "std::unordered_map" << std::endl;
for( const auto & n : u)
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
std::cout << std::endl;
std::cout << "std::map" << std::endl;
for( const auto & n : v)
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
}
```
The final output is:
```txt
std::unordered_map
Key:[2] Value:[2]
Key:[3] Value:[3]
Key:[1] Value:[1]
std::map
Key:[1] Value:[1]
Key:[2] Value:[2]
Key:[3] Value:[3]
```
## 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`
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.
### Basic Operations
There are three core functions for the use of tuples:
1. `std::make_tuple`: construct tuple
2. `std::get`: Get the value of a position in the tuple
3. `std::tie`: tuple unpacking
```cpp
#include <tuple>
#include <iostream>
auto get_student(int id) {
if (id == 0)
return std::make_tuple(3.8, 'A', "John");
if (id == 1)
return std::make_tuple(2.9, 'C', "Jack");
if (id == 2)
return std::make_tuple(1.7, 'D', "Ive");
// it is not allowed to return 0 directly
// return type is std::tuple<double, char, std::string>
return std::make_tuple(0.0, 'D', "null");
}
int main() {
auto student = get_student(0);
std::cout << "ID: 0, "
<< "GPA: " << std::get<0>(student) << ", "
<< "Grade: " << std::get<1>(student) << ", "
<< "Name: " << std::get<2>(student) << '\n';
double gpa;
char grade;
std::string name;
// unpack tuples
std::tie(gpa, grade, name) = get_student(1);
std::cout << "ID: 1, "
<< "GPA: " << gpa << ", "
<< "Grade: " << grade << ", "
<< "Name: " << name << '\n';
}
```
`std::get` In addition to using constants to get tuple objects, C++14 adds usage types to get objects in tuples:
```cpp
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
std::cout << std::get<std::string>(t) << std::endl;
std::cout << std::get<double>(t) << std::endl; // illegal, runtime error
std::cout << std::get<3>(t) << std::endl;
```
### Runtime Indexing
If you think about it, you might find the problem with the above code. `std::get<>` depends on a compile-time constant, so the following is not legal:
```cpp
int index = 1;
std::get<index>(t);
```
So what do you do? The answer is to use `std::variant<>` (introduced by C++ 17) to provide type template parameters for `variant<>`
You can have a `variant<>` to accommodate several types of variables provided (in other languages, such as Python/JavaScript, etc., as dynamic types):
```cpp
#include <variant>
template <size_t n, typename... T>
constexpr std::variant<T...> _tuple_index(const std::tuple<T...>& tpl, size_t i) {
if constexpr (n >= sizeof...(T))
throw std::out_of_range("越界.");
if (i == n)
return std::variant<T...>{ std::in_place_index<n>, std::get<n>(tpl) };
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(tpl, i);
}
template <typename... T>
constexpr std::variant<T...> tuple_index(const std::tuple<T...>& tpl, size_t i) {
return _tuple_index<0>(tpl, i);
}
template <typename T0, typename ... Ts>
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
std::visit([&](auto && x){ s << x;}, v);
return s;
}
```
So we can:
```cpp
int i = 1;
std::cout << tuple_index(t, i) << std::endl;
```
### Merge and Iteration
Another common requirement is to merge two tuples, which can be done with `std::tuple_cat`:
```cpp
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.
First, we need to know the length of a tuple, which can:
```cpp
template <typename T>
auto tuple_len(T &tpl) {
return std::tuple_size<T>::value;
}
```
This will iterate over the tuple:
```cpp
for(int i = 0; i != tuple_len(new_tuple); ++i)
// runtime indexing
std::cout << tuple_index(new_tuple, i) << std::endl;
```
## 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, 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.
[Table of Content](./toc.md) | [Previous Chapter](./03-runtime.md) | [Next Chapter: Smart Pointers and Memory Management](./05-pointers.md)
## Licenses
<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).
================================================
FILE: book/en-us/05-pointers.md
================================================
---
title: "Chapter 05 Smart Pointers and Memory Management"
type: book-en-us
order: 5
---
# Chapter 05 Smart Pointers and Memory Management
[TOC]
## 5.1 RAII and Reference Counting
Programmers who understand `Objective-C`/`Swift`/`JavaScript` should know the concept of reference counting. The reference count is counted to prevent memory leaks.
The basic idea is to count the number of dynamically allocated objects. Whenever you add a reference to the same object, the reference count of the referenced object is incremented once.
Each time a reference is deleted, the reference count is decremented by one. When the reference count of an object is reduced to zero, the pointed heap memory is automatically deleted.
In traditional C++, "remembering" to manually release resources is not always a best practice. Because we are likely to forget to release resources and lead to leakage.
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.
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>`.
> 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 indicate the life cycle of resources.
## 5.2 `std::shared_ptr`
`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.
`std::make_shared` can be used to eliminate the explicit use of `new`, so `std::make_shared` will allocate the objects in the generated parameters.
And return the `std::shared_ptr` pointer of this object type. For example:
```cpp
#include <iostream>
#include <memory>
void foo(std::shared_ptr<int> i) {
(*i)++;
}
int main() {
// auto pointer = new int(10); // illegal, no direct assignment
// Constructed a std::shared_ptr
auto pointer = std::make_shared<int>(10);
foo(pointer);
std::cout << *pointer << std::endl; // 11
// The shared_ptr will be destructed before leaving the scope
return 0;
}
```
`std::shared_ptr` can get the raw pointer through the `get()` method and reduce the reference count by `reset()`.
And see the reference count of an object by `use_count()`. E.g:
```cpp
auto pointer = std::make_shared<int>(10);
auto pointer2 = pointer; // reference count+1
auto pointer3 = pointer; // reference count+1
int *p = pointer.get(); // no increase of reference count
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
pointer2.reset();
std::cout << "reset pointer2:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
std::cout << "pointer2.use_count() = "
<< pointer2.use_count() << std::endl; // pointer2 has reset, 0
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
pointer3.reset();
std::cout << "reset pointer3:" << std::endl;
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
std::cout << "pointer3.use_count() = "
<< pointer3.use_count() << std::endl; // pointer3 has reset, 0
```
## 5.3 `std::unique_ptr`
`std::unique_ptr` is an exclusive smart pointer that prohibits other smart pointers from sharing the same object, thus keeping the code safe:
```cpp
std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique, from C++14
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:
>
> ```cpp
> template<typename T, typename ...Args>
> std::unique_ptr<T> make_unique( Args&& ...args ) {
> return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
> }
> ```
>
> As for why it wasn't provided, Herb Sutter, chairman of the C++ Standards Committee, mentioned in his [blog](https://herbsutter.com/gotw/_102/) that it was because they were forgotten.
Since it is monopolized, in other words, it cannot be copied. However, we can use `std::move` to transfer it to other `unique_ptr`, for example:
```cpp
#include <iostream>
#include <memory>
struct Foo {
Foo() { std::cout << "Foo::Foo" << std::endl; }
~Foo() { std::cout << "Foo::~Foo" << std::endl; }
void foo() { std::cout << "Foo::foo" << std::endl; }
};
void f(const Foo &) {
std::cout << "f(const Foo&)" << std::endl;
}
int main() {
std::unique_ptr<Foo> p1(std::make_unique<Foo>());
// p1 is not empty, prints
if (p1) p1->foo();
{
std::unique_ptr<Foo> p2(std::move(p1));
// p2 is not empty, prints
f(*p2);
// p2 is not empty, prints
if(p2) p2->foo();
// p1 is empty, no prints
if(p1) p1->foo();
p1 = std::move(p2);
// p2 is empty, no prints
if(p2) p2->foo();
std::cout << "p2 was destroyed" << std::endl;
}
// p1 is not empty, prints
if (p1) p1->foo();
// Foo instance will be destroyed when leaving the scope
}
```
## 5.4 `std::weak_ptr`
If you think about `std::shared_ptr` carefully, you will still find that there is still a problem that resources cannot be released. Look at the following example:
```cpp
#include <iostream>
#include <memory>
class A;
class B;
class A {
public:
std::shared_ptr<B> pointer;
~A() {
std::cout << "A was destroyed" << std::endl;
}
};
class B {
public:
std::shared_ptr<A> pointer;
~B() {
std::cout << "B was destroyed" << std::endl;
}
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->pointer = b;
b->pointer = a;
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` 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:

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 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
The technology of smart pointers is not novel. It is a common technology in many languages. Modern C++ introduces this technology, which eliminates the abuse of `new`/`delete` to a certain extent. It is a more mature technology. Programming paradigm.
[Table of Content](./toc.md) | [Previous Chapter](./04-containers.md) | [Next Chapter: Regular Expression](./06-regex.md)
## Further Readings
- [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
<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).
================================================
FILE: book/en-us/06-regex.md
================================================
---
title: "Chapter 06 Regular Expression"
type: book-en-us
order: 6
---
# Chapter 06 Regular Expression
[TOC]
## 6.1 Introduction
Regular expressions are not part of the C++ language and therefore we only briefly
introduced it here.
Regular expressions describe a pattern of string matching.
The general use of regular expressions is mainly to achieve
the following three requirements:
1. Check if a string contains some form of substring;
2. Replace the matching substrings;
3. Take the eligible substring from a string.
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.
Regular expressions act as a template to match a character pattern to the string being searched.
### Ordinary characters
Normal characters include all printable and unprintable characters that are not explicitly specified as metacharacters. This includes all uppercase
and lowercase letters, all numbers, all punctuation, and some other symbols.
### Special characters
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:
| Symbol | Description |
|:----------------:|:---|
| `$` | Matches the end position of the input string.|
| `(`,`)` | 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 one or more times.|
| `.` | Matches any single character except the newline character `\n`.|
| `[` | Marks the beginning of a bracket expression.|
| `?` | 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. |
| `^` | 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.|
| `\|` | Indicates a choice between the two.|
### 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:
| 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 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}`.|
| `{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,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.
## 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 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 regular expression library provided by C++11 operates on the `std::string` object, and the pattern `std::regex` (essentially `std::basic_regex`) is initialized and matched by `std::regex_match` Produces `std::smatch` (essentially the `std::match_results` object).
We use a simple example to briefly introduce the use of this library. Consider the following regular expression:
- `[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:
```cpp
#include <iostream>
#include <string>
#include <regex>
int main() {
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 `\\.`
std::regex txt_regex("[a-z]+\\.txt");
for (const auto &fname: fnames)
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
}
```
Another common form is to pass in the three arguments `std::string`/`std::smatch`/`std::regex`.
The essence of `std::smatch` is actually `std::match_results`.
In the standard library, `std::smatch` is defined as `std::match_results<std::string::const_iterator>`,
which means `match_results` of a substring iterator type.
Use `std::smatch` to easily get the matching results, for example:
```cpp
std::regex base_regex("([a-z]+)\\.txt");
std::smatch base_match;
for(const auto &fname: fnames) {
if (std::regex_match(fname, base_match, base_regex)) {
// the first element of std::smatch matches the entire string
// the second element of std::smatch matches the first expression
// with brackets
if (base_match.size() == 2) {
std::string base = base_match[1].str();
std::cout << "sub-match[0]: " << base_match[0].str() << std::endl;
std::cout << fname << " sub-match[1]: " << base << std::endl;
}
}
}
```
The output of the above two code snippets is:
```
foo.txt: 1
bar.txt: 1
test: 0
a0.txt: 0
AAA.txt: 0
sub-match[0]: foo.txt
foo.txt sub-match[1]: foo
sub-match[0]: bar.txt
bar.txt sub-match[1]: bar
```
## Conclusion
This section briefly introduces the regular expression itself,
and then introduces the use of the regular expression library
through a practical example based on the main requirements of
using regular expressions.
## Exercise
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.
Given the following request structure:
```cpp
struct Request {
// request method, POST, GET; path; HTTP version
std::string method, path, http_version;
// use smart pointer for reference counting of content
std::shared_ptr<std::istream> content;
// hash container, key-value dict
std::unordered_map<std::string, std::string> header;
// use regular expression for path match
std::smatch path_match;
};
```
Requested resource type:
```cpp
typedef std::map<
std::string, std::unordered_map<
std::string,std::function<void(std::ostream&, Request&)>>> resource_type;
```
And server template:
```cpp
template <typename socket_type>
class ServerBase {
public:
resource_type resource;
resource_type default_resource;
void start() {
// TODO
}
protected:
Request parse_request(std::istream& stream) const {
// TODO
}
}
```
Please implement the member functions `start()` and `parse_request`. Enable server template users to specify routes as follows:
```cpp
template<typename SERVER_TYPE>
void start_server(SERVER_TYPE &server) {
// process GET request for /match/[digit+numbers],
// 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];
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 response files in folder web/
// default: index.html
server.default_resource["fill_your_reg_ex"]["GET"] =
[](ostream& response, Request& request)
{
string filename = "www/";
string path = request.path_match[1];
// forbidden use `..` access content outside folder web/
size_t last_pos = path.rfind(".");
size_t current_pos = 0;
size_t pos;
while((pos=path.find('.', current_pos)) != string::npos && pos != last_pos) {
current_pos = pos;
path.erase(pos, 1);
last_pos--;
}
// (...)
};
server.start();
}
```
An suggested solution can be found [here](../../exercises/6).
[Table of Content](./toc.md) | [Previous Chapter](./05-pointers.md) | [Next Chapter: Threads and Concurrency](./07-thread.md)
## Further Readings
1. [Comments from `std::regex`'s author](https://zhihu.com/question/23070203/answer/84248248)
2. [Library document of Regular Expression](https://en.cppreference.com/w/cpp/regex)
## Licenses
<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).
================================================
FILE: book/en-us/07-thread.md
================================================
---
title: "Chapter 07 Parallelism and Concurrency"
type: book-en-us
order: 7
---
# Chapter 07 Parallelism and Concurrency
[TOC]
## 7.1 Basic of Parallelism
`std::thread` is used to create an execution thread instance, so it is the basis for all concurrent programming. It needs to include the `<thread>` header file when using it.
It provides a number of basic thread operations, such as `get_id()` to get the thread ID of the thread being created, use `join()` to join a thread, etc., for example:
```cpp
#include <iostream>
#include <thread>
int main() {
std::thread t([](){
std::cout << "hello world." << std::endl;
});
t.join();
return 0;
}
```
## 7.2 Mutex and Critical Section
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.
`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.
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.
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 keeping the simplicity of the code.
```cpp
#include <iostream>
#include <mutex>
#include <thread>
int v = 1;
void critical_section(int change_v) {
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
// execute contention works
v = change_v;
// mtx will be released after leaving the scope
}
int main() {
std::thread t1(critical_section, 2), t2(critical_section, 3);
t1.join();
t2.join();
std::cout << v << std::endl;
return 0;
}
```
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 unwinding is thrown, and `unlock()` is automatically called.
> 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`.
`std::lock_guard` cannot explicitly call `lock` and `unlock`, and `std::unique_lock` can be called anywhere after the declaration.
It can reduce the scope of the lock and provide higher concurrency.
If you use the condition variable `std::condition_variable::wait` you must use `std::unique_lock` as a parameter.
For instance:
```cpp
#include <iostream>
#include <mutex>
#include <thread>
int v = 1;
void critical_section(int change_v) {
static std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx);
// do contention operations
v = change_v;
std::cout << v << std::endl;
// release the lock
lock.unlock();
// during this period,
// others are allowed to acquire v
// start another group of contention operations
// lock again
lock.lock();
v += 1;
std::cout << v << std::endl;
}
int main() {
std::thread t1(critical_section, 2), t2(critical_section, 3);
t1.join();
t2.join();
return 0;
}
```
## 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.
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.
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.
Before the introduction of `std::future` in C++11, the usual practice is:
Create a thread A, start task B in thread A, send an event when it is ready, and save the result in a global variable.
The main function thread A is doing other things. When the result is needed, a thread is called to wait for the function to get the result of the execution.
The `std::future` provided by C++11 simplifies this process and can be used to get the results of asynchronous tasks.
Naturally, we can easily imagine it as a simple means of thread synchronization, namely the barrier.
To see an example, we use extra `std::packaged_task`, which can be used to wrap any target that can be called for asynchronous calls. For example:
```cpp
#include <iostream>
#include <thread>
#include <future>
int main() {
// pack a lambda expression that returns 7 into a std::packaged_task
std::packaged_task<int()> task([](){return 7;});
// get the future of task
std::future<int> result = task.get_future(); // run task in a thread
std::thread(std::move(task)).detach();
std::cout << "waiting...";
result.wait(); // block until future has arrived
// output result
std::cout << "done!" << std:: endl << "future result is "
<< result.get() << std::endl;
return 0;
}
```
After encapsulating the target to be called, you can use `get_future()` to get a `std::future` object to implement thread synchronization later.
## 7.4 Condition Variable
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.
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` object is created primarily to wake up the waiting thread and avoid deadlocks.
`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:
```cpp
#include <queue>
#include <chrono>
#include <mutex>
#include <thread>
#include <iostream>
#include <condition_variable>
int main() {
std::queue<int> produced_nums;
std::mutex mtx;
std::condition_variable cv;
bool notified = false; // notification sign
auto producer = [&]() {
for (int i = 0; ; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::unique_lock<std::mutex> lock(mtx);
std::cout << "producing " << i << std::endl;
produced_nums.push(i);
notified = true;
cv.notify_all();
}
};
auto consumer = [&]() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
while (!notified) { // avoid spurious wakeup
cv.wait(lock);
}
// temporal unlock to allow producer produces more rather than
// let consumer hold the lock until its consumed.
lock.unlock();
// consumer is slower
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
lock.lock();
if (!produced_nums.empty()) {
std::cout << "consuming " << produced_nums.front() << std::endl;
produced_nums.pop();
}
notified = false;
}
};
std::thread p(producer);
std::thread cs[2];
for (int i = 0; i < 2; ++i) {
cs[i] = std::thread(consumer);
}
p.join();
for (int i = 0; i < 2; ++i) {
cs[i].join();
}
return 0;
}
```
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, 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 produce content in a parallel consumer queue, and we still need a more granular approach.
## 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.
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 the memory model introduced from C++11. Let's first look at a question. What is the output of the following code?
```cpp
#include <thread>
#include <iostream>
int main() {
int a = 0;
volatile int flag = 0;
std::thread t1([&]() {
while (flag != 1);
int b = a;
std::cout << "b = " << b << std::endl;
});
std::thread t2([&]() {
a = 5;
flag = 1;
});
t1.join();
t2.join();
return 0;
}
```
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.
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.
### Atomic Operation
`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:
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
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).
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, 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,
and minimize an atomic read or write operation from a set of instructions to a single CPU instruction. E.g:
```cpp
std::atomic<int> counter;
```
And provides basic numeric member functions for atomic types of integers or floating-point numbers, for example,
Including `fetch_add`, `fetch_sub`, etc., and the corresponding `+`, `-` version is provided by overload.
For example, the following example:
```cpp
#include <atomic>
#include <thread>
#include <iostream>
std::atomic<int> count = {0};
int main() {
std::thread t1([](){
count.fetch_add(1);
});
std::thread t2([](){
count++; // identical to fetch_add
count += 1; // identical to fetch_add
});
t1.join();
t2.join();
std::cout << count << std::endl;
return 0;
}
```
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
#include <atomic>
#include <iostream>
struct A {
float x;
int y;
long long z;
};
int main() {
std::atomic<A> a;
std::cout << std::boolalpha << a.is_lock_free() << std::endl;
return 0;
}
```
### Consistency Model
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.
If we force the operation of a variable `v` between multiple threads to be atomic, that is, any thread after the operation of `v`
Other threads can **synchronize** to perceive changes in `v`, for the variable `v`, which appears as a sequential execution of the program, it does not have any efficiency gains due to the introduction of multithreading. Is there any way to accelerate this properly? The answer is to weaken the synchronization conditions between processes in atomic operations.
In principle, each thread can correspond to a cluster node, and communication between threads is almost equivalent to communication between cluster nodes.
Weakening the synchronization conditions between processes, usually we will consider four different consistency models:
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()
T1 ---------+----------------+------>
T2 -------------------+------------->
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.
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()
T1 ---------+-----------+----------+----->
T2 ---------------+---------------------->
x.store(2)
or
x.store(1) x.store(3) x.load()
T1 ---------+-----------+----------+----->
T2 ------+------------------------------->
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, 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.
```
a = 1 b = 2
T1 ----+-----------+---------------------------->
T2 ------+--------------------+--------+-------->
x.store(3) c = a + b y.load()
or
a = 1 b = 2
T1 ----+-----------+---------------------------->
T2 ------+--------------------+--------+-------->
x.store(3) y.load() c = a + b
or
b = 2 a = 1
T1 ----+-----------+---------------------------->
T2 ------+--------------------+--------+-------->
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`)
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)
T1 ----+-----------+-------------------------------------------->
T2 ---------+------------+--------------------+--------+-------->
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:
```
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 0 0 4 // The last read read the final value of x,
// 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
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:
```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_relaxed);
});
}
for (auto& t : vt) {
t.join();
}
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:
```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)));
std::cout << "p: " << *p << std::endl;
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 is visible to any other thread, i.e., 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.
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);
std::cout << v.at(0) << std::endl; // must be 42
});
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`. However, in most cases, `compare_exchange_weak` is discouraged due to the complexity of its usage.
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:
```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);
});
}
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.
## 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.
In addition, we also introduced the "memory model" as one of the most important features of C++11.
They provide a critical foundation for standardized high-performance computing for C++.
## Exercises
1. Write a simple thread pool that provides the following features:
```cpp
ThreadPool p(4); // specify four work thread
// enqueue a task, and return a std::future
auto f = pool.enqueue([](int life) {
return meaning;
}, 42);
// fetch result from future
std::cout << f.get() << std::endl;
```
2. Use `std::atomic<bool>` to implement a mutex.
[Table of Content](./toc.md) | [Previous Chapter](./06-regex.md) | [Next Chapter: File System](./08-filesystem.md)
## Further Readings
- [C++ Concurrency in Action](https://www.amazon.com/dp/1617294691/ref=cm_sw_em_r_mt_dp_U_siEmDbRMMF960)
- [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
## Licenses
<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).`
================================================
FILE: book/en-us/08-filesystem.md
================================================
---
title: "Chapter 08 File System"
type: book-en-us
order: 8
---
# Chapter 08 File System
[TOC]
The file system library provides functions related to
the operation of the file system, path, regular files, directories, and so on.
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.
## 8.1 Document and Link
TODO:
## 8.2 std::filesystem
TODO:
[Table of Content](./toc.md) | [Previous Chapter](./07-thread.md) | [Next Chapter: Minor Features](./09-others.md)
## Further Readings
## Licenses
<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).
================================================
FILE: book/en-us/09-others.md
================================================
---
title: Chapter 09 Minor Features
type: book-en-us
order: 9
---
# Chapter 09 Minor Features
[TOC]
## 9.1 New Type
### `long long int`
`long long int` is not the first to be introduced in C++11.
As early as C99, `long long int` has been included in the C standard,
so most compilers already support it.
C++11 now formally incorporate it into the standard library,
specifying a `long long int` type with at least 64 bits.
## 9.2 `noexcept` and Its Operations
One of the big advantages of C++ over C is that
C++ itself defines a complete set of exception handling mechanisms.
However, before C++11, almost no one used to write an exception declaration expression after the function name.
Starting from C++11, this mechanism was deprecated,
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.
C++11 simplifies exception declarations into two cases:
1. The function may throw any exceptions
2. The function can't throw any exceptions
And use `noexcept` to limit these two behaviors, for example:
```cpp
void may_throw(); // May throw any exception
void no_throw() noexcept; // Cannot throw any exception
```
If a function modified with `noexcept` is thrown,
the compiler will use `std::terminate()` to
immediately terminate the program.
`noexcept` can also be used as an operator to manipulate an expression.
When the expression has no exception, it returns `true`,
otherwise, it returns `false`.
```cpp
#include <iostream>
void may_throw() {
throw true;
}
auto non_block_throw = []{
may_throw();
};
void no_throw() noexcept {
return;
}
auto block_throw = []() noexcept {
no_throw();
};
int main()
{
std::cout << std::boolalpha
<< "may_throw() noexcept? " << noexcept(may_throw()) << std::endl
<< "no_throw() noexcept? " << noexcept(no_throw()) << std::endl
<< "lmay_throw() noexcept? " << noexcept(non_block_throw()) << std::endl
<< "lno_throw() noexcept? " << noexcept(block_throw()) << std::endl;
return 0;
}
```
`noexcept` can modify the function of blocking exceptions
after modifying a function. If an exception is generated internally,
the external will not trigger. For instance:
```cpp
try {
may_throw();
} catch (...) {
std::cout << "exception captured from may_throw()" << std::endl;
}
try {
non_block_throw();
} catch (...) {
std::cout << "exception captured from non_block_throw()" << std::endl;
}
try {
block_throw();
} catch (...) {
std::cout << "exception captured from block_throw()" << std::endl;
}
```
The final output is:
```
exception captured, from may_throw()
exception captured, from non_block_throw()
```
## 9.3 Literal
### Raw String Literal
In traditional C++, it is very painful to write a string full of
special characters. For example, a string containing HTML ontology
needs to add a large number of escape characters.
For example, a file path on Windows often as: `C:\\Path\\To\\File`.
C++11 provides the original string literals,
which can be decorated with `R` in front of a string,
and the original string is wrapped in parentheses, for example:
```cpp
#include <iostream>
#include <string>
int main() {
std::string str = R"(C:\Path\To\File)";
std::cout << str << std::endl;
return 0;
}
```
### Custom Literal
C++11 introduces the ability to customize literals by
overloading the double quotes suffix operator:
```cpp
// String literal customization must be set to the following parameter list
std::string operator"" _wow1(const char *wow1, size_t len) {
return std::string(wow1)+"woooooooooow, amazing";
}
std::string operator"" _wow2 (unsigned long long i) {
return std::to_string(i)+"woooooooooow, amazing";
}
int main() {
auto str = "abc"_wow1;
auto num = 1_wow2;
std::cout << str << std::endl;
std::cout << num << std::endl;
return 0;
}
```
Custom literals support four literals:
1. Integer literal: When overloading, you must use `unsigned long long`, `const char *`, and template literal operator parameters. The former is used in the above code;
2. Floating-point literals: You must use `long double`, `const char *`, and template literals when overloading;
3. String literals: A parameter table of the form `(const char *, size_t)` must be used;
4. Character literals: Parameters can only be `char`, `wchar_t`, `char16_t`, `char32_t`.
## 9.4 Memory Alignment
C++ 11 introduces two new keywords, `alignof` and `alignas`, to support control of memory alignment.
The `alignof` keyword can get a platform-dependent value of type `std::size_t` to query the alignment of the platform.
Of course, we are sometimes not satisfied with this, and even want to customize the alignment of the structure. Similarly, C++ 11 introduces `alignas`.
To reshape the alignment of a structure. Let's look at two examples:
```cpp
#include <iostream>
struct Storage {
char a;
int b;
double c;
long long d;
};
struct alignas(std::max_align_t) AlignasStorage {
char a;
int b;
double c;
long long d;
};
int main() {
std::cout << alignof(Storage) << std::endl;
std::cout << alignof(AlignasStorage) << std::endl;
return 0;
}
```
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.
## Conclusion
Several of the features introduced in this section are those that
use more frequent features from modern C++ features that
have not yet been introduced. `noexcept` is the most important feature.
One of its features is to prevent the spread of anomalies,
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)
## Licenses
<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).
================================================
FILE: book/en-us/10-cpp20.md
================================================
---
title: "Chapter 10 Outlook: Introduction of C++20"
type: book-en-us
order: 10
---
# Chapter 10 Outlook: Introduction of C++20
[TOC]
C++20 seems to be an exciting update.
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.
The C++ Organizing Committee decided to vote to finalize C++20 with many proposals,
such as **Concepts**/**Module**/**Coroutine**/**Ranges**/ and so on.
In this chapter, we'll take a look at some of the important features that
C++20 will introduce.
## Concept
The concept is a further enhancement to C++ template programming.
In simple terms, the concept is a compile-time feature.
It allows the compiler to evaluate template parameters at compile-time,
greatly enhancing our experience with template programming in C++.
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.
For example, the following two lines of code can cause a lot of
almost unreadable compilation errors:
```cpp
#include <list>
#include <algorithm>
int main() {
std::list<int> l = {1, 2, 3};
std::sort(l.begin(), l.end());
return 0;
}
```
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,
and we know that `std::list` does not support random access.
In the conceptual language, the iterator in `std::list` does not satisfy
the constraint of the concept of random iterators in `std::sort`.
After introducing the concept, we can constrain the template parameters
like this:
```cpp
template <typename T>
requires Sortable<T> // Sortable is a concept
void sort(T& c);
```
abbreviate as:
```cpp
template<Sortable T> // T is a Sortable typename
void sort(T& c)
```
Even use it directly as a type:
```cpp
void sort(Sortable& c); // c is a Sortable type object
```
Let's look at a practical example.
TODO:
## Module
TODO:
## Contract
TODO:
## Range
TODO:
## Coroutine
TODO:
## Conclusion
In general, I finally saw the exciting features of Concepts/Ranges/Modules in C++20.
This is still full of charm for a programming language that is already in its thirties.
[Table of Content](./toc.md) | [Previous Chapter](./09-others.md) | [Next Chapter](./appendix1.md)
## Further Readings
- [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](https://en.cppreference.com/w/cpp/compiler_support)
- [C++ History](https://en.cppreference.com/w/cpp/language/history)
## Licenses
<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).
================================================
FILE: book/en-us/appendix1.md
================================================
---
title: "Appendix 1: Further Study Materials"
type: book-en-us
order: 11
---
# Appendix 1: Further Study Materials
First of all, congratulations 🎉 on reading this book! I hope this book has raised your interest in modern C++.
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](https://en.cppreference.com/w)
- [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)
- to be added
[Table of Content](./toc.md) | [Previous Chapter](./10-cpp20.md) | [Next Chapter](./appendix2.md)
## Licenses
<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).
================================================
FILE: book/en-us/appendix2.md
================================================
---
title: "Appendix 2: Modern C++ Best Practices"
type: book-en-us
order: 12
---
# Appendix 2: Modern C++ Best Practices
In this appendix we will briefly talk about the best practices of modern C++. In general, the author's thoughts on C++'s best practices are mainly absorbed from [Effective Modern C++](https://www.amazon.com/dp/1491903996/ref=cm_sw_em_r_mt_dp_U_-ZgjDb81ERBNP) and [C++ Style Guide](https://google.github.io/styleguide/cppguide.html). In this appendix, we will briefly discuss and use the actual examples to illustrate the methods, and introduce some of **the author's personal**, **non-common**, **non-sensible** best practices, and how to ensure the overall quality of the code.
## Common Tools
TODO:
## Coding Style
TODO:
## Overall Performance
TODO:
## Code Security
TODO:
## Maintainability
TODO:
## Portability
TODO:
[Table of Content](./toc.md) | [Previous Chapter](./appendix1.md)
## Licenses
<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).
================================================
FILE: book/en-us/toc.md
================================================
# C++ 11/14/17/20 On The Fly
## Table of Contents
- [**Preface**](./00-preface.md)
- [**Chapter 01 Towards Modern C++**](./01-intro.md)
+ 1.1 Deprecated Features
+ 1.2 Compatibility with C
+ Further Readings
- [**Chapter 02 Language Usability Enhancements**](./02-usability.md)
+ 2.1 Constants
- nullptr
- constexpr
+ 2.2 Variables & Initialization
- Conditional Statement
- Initializer List
- Structured binding
+ 2.3 Type Deduction
- auto
- decltype
- Tail return type
- decltype(auto)
+ 2.4 Control Flow
- if constexpr
- Range-based for loop
+ 2.5 Templates
- External templates
- The ">"
- Type alias templates
- Default template parameters
- Variadic templates
- Fold expression
- Non-type template parameter deduction
+ 2.6 Object-oriented
- Delegate constructor
- Inheritance constructor
- Explicit virtual function overwrite
- override
- final
- Explicit delete default function
- Strongly typed enumerations
- [**Chapter 03 Language Runtime Enhancements**](./03-runtime.md)
+ 3.1 Lambda expression
+ Basics
+ Generics
+ 3.2 Function object wrapper
+ std::function
+ std::bind/std::placeholder
+ 3.3 rvalue reference
+ lvalue, rvalue, prvalue, xvalue
+ rvalue reference and lvalue reference
+ Move semantics
+ Perfect forwarding
- [**Chapter 04 Containers**](./04-containers.md)
+ 4.1 Linear containers
+ `std::array`
+ `std::forward_list`
+ 4.2 Unordered containers
+ `std::unordered_set`
+ `std::unordered_map`
+ 4.3 Tuples `std::tuple`
+ basic operation
+ runtime indexing `std::variant`
+ merge and iteration
- [**Chapter 05 Smart Pointers and Memory Management**](./05-pointers.md)
+ 5.1 RAII and reference counting
+ 5.2 `std::shared_ptr`
+ 5.3 `std::unique_ptr`
+ 5.4 `std::weak_ptr`
- [**Chapter 06 Regular Expression**](./06-regex.md)
+ 6.1 Introduction
+ Ordinary characters
+ Special characters
+ Quantifiers
+ 6.2 `std::regex` and its related
+ `std::regex`
+ `std::regex_match`
+ `std::match_results`
- [**Chapter 07 Parallelism and Concurrency**](./07-thread.md)
+ 7.1 Basic of Parallelism
+ 7.2 Mutex and Critical Section
+ 7.3 Futures
+ 7.4 Condition Variable
+ 7.5 Atomic Operation and Memory Model
+ Atomic Operation
+ Consistency Model
+ Memory Orders
- [**Chapter 08 File System**](./08-filesystem.md)
+ 8.1 Documents and links
+ 8.2 `std::filesystem`
- [**Chapter 09 Minor Features**](./09-others.md)
+ 9.1 New Types
+ `long long int`
+ 9.2 `noexcept` and Its Operations
+ 9.3 Literal
+ Raw String Literal
+ Custom String Literal
+ 9.4 Memory Alignment
- [**Chapter 10 Outlook: Introduction of C++20**](./10-cpp20.md)
+ 10.1 Concept
+ 10.2 Range
+ 10.3 Module
+ 10.4 Coroutine
+ 10.5 Transaction Memory
- [**Appendix 1: Further Study Materials**](./appendix1.md)
- [**Appendix 2: Modern C++ Best Practices**](./appendix2.md)
Table of Content | Last Chapter | [Next Chapter: Preface](./00-preface.md)
## Licenses
<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).
================================================
FILE: book/zh-cn/00-preface.md
================================================
---
title: 序言
type: book-zh-cn
order: 0
---
# 序言
[TOC]
## 引言
C++ 是一个用户群体相当大的语言。从 C++98 的出现到 C++11 的正式定稿经历了长达十年多之久的积累。C++14/17 则是作为对 C++11 的重要补充和优化,C++20 则将这门语言领进了现代化的大门,所有这些新标准中扩充的特性,给 C++ 这门语言注入了新的活力。
那些还在坚持使用**传统 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++17 则是近三年依赖 C++ 社区一致推进的方向,也指出了 **现代C++** 编程的一个重要发展方向。尽管它的出现并不如 C++11 的分量之重,但它包含了大量小而美的语言与特性(例如结构化绑定),这些特性的出现再一次修正了我们在 C++ 中的编程范式。
现代 C++ 还为自身的标准库增加了非常多的工具和方法,诸如在语言自身标准的层面上制定了 `std::thread`,从而支持了并发编程,在不同平台上不再依赖于系统底层的 API,实现了语言层面的跨平台支持;`std::regex` 提供了完整的正则表达式支持等等。C++98 已经被实践证明了是一种非常成功的『范型』,而现代 C++ 的出现,则进一步推动这种范型,让 C++ 成为系统程序设计和库开发更好的语言。Concept 提供了对模板参数编译期的检查,进一步增强了语言整体的可用性。
总而言之,我们作为 C++ 的拥护与实践者,始终保持接纳新事物的开放心态,才能更快的推进 C++ 的发展,使得这门古老而又新颖的语言更加充满活力。
## 目标读者
1. 本书假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++ 进行编码的人、渴望在短时间内迅速了解**现代 C++** 特性的人非常适合阅读本书;
2. 本书一定程度上介绍了一些现代 C++ 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习现代 C++ 的读者,本书的定位系**现代 C++ 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 **现代 C++** 的熟悉度。
## 本书目的
本书号称『高速上手』,从内容上对二十一世纪二十年代之前产生 C++ 的相关特性做了非常相对全面的介绍,读者可以自行根据下面的目录选取感兴趣的内容进行学习,快速熟悉需要了解的内容。这些特性并不需要全部掌握,只需针对自己的使用需求和特定的应用场景,学习、查阅最适合自己的新特性即可。
同时,本书在介绍这些特性的过程中,尽可能简单明了的介绍了这些特性产生的历史背景和技术需求,这为理解这些特性、运用这些特性提供了很大的帮助。
此外,笔者希望读者在阅读本书后,能够努力在新项目中直接使用 C++17,并努力将旧项目逐步迁移到 C++17。也算是笔者为推进现代 C++ 的普及贡献了一些绵薄之力。
## 相关代码
本书每章中都出现了大量的代码,如果你在跟随本书介绍特性的思路编写自己的代码遇到问题时,不妨读一读随书附上的源码,你可以在[这里](../../code)中找到书中介绍过的全部的源码,所有代码按章节组织,文件夹名称为章节序号。
## 随书习题
本书每章最后还加入了少量难度极小的习题,仅用于检验你是否能混合运用当前章节中的知识点。你可以在[这里](../../exercises)找到习题的答案,文件夹名称为章节序号。
[返回目录](./toc.md) | [下一章 迈向现代 C++](./01-intro.md)
## 许可
<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 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
================================================
FILE: book/zh-cn/01-intro.md
================================================
---
title: 第 1 章 迈向现代 C++
type: book-zh-cn
order: 1
---
# 第 1 章 迈向现代 C++
[TOC]
**编译环境**:本书将使用 `clang++` 作为唯一使用的编译器,同时总是在代码中使用 `-std=c++2a` 编译标志。
```bash
> clang++ -v
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
```
## 1.1 被弃用的特性
在学习现代 C++ 之前,我们先了解一下从 C++11 开始,被弃用的主要特性:
> **注意**:弃用并非彻底不能用,只是用于暗示程序员这些特性将从未来的标准中消失,应该尽量避免使用。但是,已弃用的特性依然是标准库的一部分,并且出于兼容性的考虑,大部分特性其实会『永久』保留。
- **不再允许字符串字面值常量赋值给一个 `char *`。如果需要用字符串字面值常量赋值和初始化一个 `char *`,应该使用 `const char *` 或者 `auto`。**
```cpp
char *str = "hello world!"; // 将出现弃用警告
```
- **C++98 异常说明、 `unexpected_handler`、`set_unexpected()` 等相关特性被弃用,应该使用 `noexcept`。**
- **`auto_ptr` 被弃用,应使用 `unique_ptr`。**
- **`register` 关键字被弃用,可以使用但不再具备任何实际含义。**
- **`bool` 类型的 `++` 操作被弃用。**
- **如果一个类有析构函数,为其生成拷贝构造函数和拷贝赋值运算符的特性被弃用了。**
- **C 语言风格的类型转换被弃用(即在变量前使用 `(convert_type)`),应该使用 `static_cast`、`reinterpret_cast`、`const_cast` 来进行类型转换。**
- **特别地,在最新的 C++17 标准中弃用了一些可以使用的 C 标准库,例如 `<ccomplex>`、`<cstdalign>`、`<cstdbool>` 与 `<ctgmath>` 等**
- ……等等
还有一些其他诸如参数绑定(C++11 提供了 `std::bind` 和 `std::function`)、`export` 等特性也均被弃用。前面提到的这些特性**如果你从未使用或者听说过,也请不要尝试去了解他们,应该向新标准靠拢**,直接学习新特性。毕竟,技术是向前发展的。
## 1.2 与 C 的兼容性
出于一些不可抗力、历史原因,我们不得不在 C++ 中使用一些 C 语言代码(甚至古老的 C 语言代码),例如 Linux 系统调用。在现代 C++ 出现之前,大部分人当谈及『C 与 C++ 的区别是什么』时,普遍除了回答面向对象的类特性、泛型编程的模板特性外,就没有其他的看法了,甚至直接回答『差不多』,也是大有人在。图 1.2 中的韦恩图大致上回答了 C 和 C++ 相关的兼容情况。

从现在开始,你的脑子里应该树立『**C++ 不是 C 的一个超集**』这个观念(而且从一开始就不是,后面的[进一步阅读的参考文献](#进一步阅读的参考文献)中给出了 C++98 和 C99 之间的区别)。在编写 C++ 时,也应该尽可能的避免使用诸如 `void*` 之类的程序风格。而在不得不使用 C 时,应该注意使用 `extern "C"` 这种特性,将 C 语言的代码与 C++代码进行分离编译,再统一链接这种做法,例如:
```cpp
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
int add(int x, int y);
#ifdef __cplusplus
}
#endif
// foo.c
int add(int x, int y) {
return x+y;
}
// 1.1.cpp
#include "foo.h"
#include <iostream>
#include <functional>
int main() {
[out = std::ref(std::cout << "Result from C code: " << add(1, 2))](){
out.get() << ".\n";
}();
return 0;
}
```
应先使用 `gcc` 编译 C 语言的代码:
```bash
gcc -c foo.c
```
编译出 `foo.o` 文件,再使用 `clang++` 将 C++ 代码和 `.o` 文件链接起来(或者都编译为 `.o` 再统一链接):
```bash
clang++ 1.1.cpp foo.o -std=c++2a -o 1.1
```
当然,你可以使用 `Makefile` 来编译上面的代码:
```makefile
C = gcc
CXX = clang++
SOURCE_C = foo.c
OBJECTS_C = foo.o
SOURCE_CXX = 1.1.cpp
TARGET = 1.1
LDFLAGS_COMMON = -std=c++2a
all:
$(C) -c $(SOURCE_C)
$(CXX) $(SOURCE_CXX) $(OBJECTS_C) $(LDFLAGS_COMMON) -o $(TARGET)
clean:
rm -rf *.o $(TARGET)
```
> 注意:`Makefile` 中的缩进是制表符而不是空格符,如果你直接复制这段代码到你的编辑器中,制表符可能会被自动替换掉,请自行确保在 `Makefile` 中的缩进是由制表符完成的。
>
> 如果你还不知道 `Makefile` 的使用也没有关系,本教程中不会构建过于复杂的代码,简单的在命令行中使用 `clang++ -std=c++2a` 也可以阅读本书。
如果你是首次接触现代 C++,那么你很可能还看不懂上面的那一小段代码,即:
```cpp
[out = std::ref(std::cout << "Result from C code: " << add(1, 2))](){
out.get() << ".\n";
}();
```
不必担心,本书的后续章节将为你介绍这一切。
[返回目录](./toc.md) | [上一章](./00-preface.md) | [下一章 语言可用性强化](./02-usability.md)
## 进一步阅读的参考文献
- [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++ 历史](https://en.cppreference.com/w/cpp/language/history)
- [C++ 特性在 GCC/Clang 等编译器中的支持情况](https://en.cppreference.com/w/cpp/compiler_support)
- [C++98 与 C99 之间的区别](http://david.tribble.com/text/cdiffs.htm#C99-vs-CPP98)
## 许可
<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 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../../LICENSE)。
================================================
FILE: book/zh-cn/02-usability.md
================================================
---
title: 第 2 章 语言可用性的强化
type: book-zh-cn
order: 2
---
# 第 2 章 语言可用性的强化
[TOC]
当我们声明、定义一个变量或者常量,对代码进行流程控制、面向对象的功能、模板编程等这些都是运行时之前,可能发生在编写代码或编译器编译代码时的行为。为此,我们通常谈及**语言可用性**,是指那些发生在运行时之前的语言行为。
## 2.1 常量
### nullptr
`nullptr` 出现的目的是为了替代 `NULL`。 C 与 C++ 语言中有**空指针常量**,它们能被隐式转换成任何指针类型的空指针值,或 C++ 中的任何成员指针类型的空成员指针值。 `NULL` 由标准库实现提供,并被定义为实现定义的空指针常量。在 C 中,有些标准库会把 `NULL` 定义为 `((void*)0)` 而有些将它定义为 `0`。
C++ **不允许**直接将 `void *` 隐式转换到其他类型,从而 `((void*)0)` 不是 `NULL` 的合法实现。如果标准库尝试把 `NULL` 定义为 `((void*)0)`,那么下面这句代码中会出现编译错误:
```cpp
char *ch = NULL;
```
没有了 `void *` 隐式转换的 C++ 只好将 `NULL` 定义为 `0`。而这依然会产生新的问题,将 `NULL` 定义成 `0` 将导致 `C++` 中重载特性发生混乱。考虑下面这两个 `foo` 函数:
```cpp
void foo(char*);
void foo(int);
```
那么 `foo(NULL);` 这个语句将会去调用 `foo(int)`,从而导致代码违反直觉。
为了解决这个问题,C++11 引入了 `nullptr` 关键字,专门用来区分空指针、`0`。而 `nullptr` 的类型为 `nullptr_t`,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。
你可以尝试使用 `clang++` 编译下面的代码:
```cpp
#include <iostream>
#include <type_traits>
void foo(char *);
void foo(int);
int main() {
if (std::is_same<decltype(NULL), decltype(0)>::value)
std::cout << "NULL == 0" << std::endl;
if (std::is_same<decltype(NULL), decltype((void*)0)>::value)
std::cout << "NULL == (void *)0" << std::endl;
if (std::is_same<decltype(NULL), std::nullptr_t>::value)
std::cout << "NULL == nullptr" << std::endl;
foo(0); // 调用 foo(int)
// foo(NULL); // 该行不能通过编译
foo(nullptr); // 调用 foo(char*)
return 0;
}
void foo(char *) {
std::cout << "foo(char*) is called" << std::endl;
}
void foo(int i) {
std::cout << "foo(int) is called" << std::endl;
}
```
将输出:
```bash
foo(int) is called
foo(char*) is called
```
从输出中我们可以看出,`NULL` 不同于 `0` 与 `nullptr`。所以,请养成直接使用 `nullptr`的习惯。
此外,在上面的代码中,我们使用了 `decltype` 和 `std::is_same` 这两个属于现代 C++ 的语法,简单来说,`decltype` 用于类型推导,而 `std::is_same` 用于比较两个类型是否相同,我们会在后面 [decltype](#decltype) 一节中详细讨论。
### constexpr
C++ 本身已经具备了常量表达式的概念,比如 `1+2`, `3*4` 这种表达式总是会产生相同的结果并且没有任何副作用。如果编译器能够在编译时就把这些表达式直接优化并植入到程序运行时,将能增加程序的性能。一个非常明显的例子就是在数组的定义阶段:
```cpp
#include <iostream>
#define LEN 10
int len_foo() {
int i = 2;
return i;
}
constexpr int len_foo_constexpr() {
return 5;
}
constexpr int fibonacci(const int n) {
return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
}
int main() {
char arr_1[10]; // 合法
char arr_2[LEN]; // 合法
int len = 10;
// char arr_3[len]; // 非法
const int len_2 = len + 1;
constexpr int len_2_constexpr = 1 + 2 + 3;
// char arr_4[len_2]; // 非法
char arr_4[len_2_constexpr]; // 合法
// char arr_5[len_foo()+5]; // 非法
char arr_6[len_foo_constexpr() + 1]; // 合法
std::cout << fibonacci(10) << std::endl;
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
std::cout << fibonacci(10) << std::endl;
return 0;
}
```
上面的例子中,`char arr_4[len_2]` 可能比较令人困惑,因为 `len_2` 已经被定义为了常量。为什么 `char arr_4[len_2]` 仍然是非法的呢?这是因为 C++ 标准中数组的长度必须是一个常量表达式,而对于 `len_2` 而言,这是一个 `const` 常数,而不是一个常量表达式,因此(即便这种行为在大部分编译器中都支持,但是)它是一个非法的行为,我们需要使用接下来即将介绍的 C++11 引入的 `constexpr` 特性来解决这个问题;而对于 `arr_5` 来说,C++98 之前的编译器无法得知 `len_foo()` 在运行期实际上是返回一个常数,这也就导致了非法的产生。
> 注意,现在大部分编译器其实都带有自身编译优化,很多非法行为在编译器优化的加持下会变得合法,若需重现编译报错的现象需要使用老版本的编译器。
C++11 提供了 `constexpr` 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式,这个关键字明确的告诉编译器应该去验证 `len_foo` 在编译期就应该是一个常量表达式。
此外,`constexpr` 修饰的函数可以使用递归:
```cpp
constexpr int fibonacci(const int n) {
return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
}
```
从 C++14 开始,`constexpr` 函数可以在内部使用局部变量、循环和分支等简单语句,例如下面的代码在 C++11 的标准下是不能够通过编译的:
```cpp
constexpr int fibonacci(const int n) {
if(n == 1) return 1;
if(n == 2) return 1;
return fibonacci(n-1) + fibonacci(n-2);
}
```
为此,我们可以写出下面这类简化的版本来使得函数从 C++11 开始即可用:
```cpp
constexpr int fibonacci(const int n) {
return n == 1 || n == 2 ? 1 : fibonacci(n-1) + fibonacci(n-2);
}
```
## 2.2 变量及其初始化
### if/switch 变量声明强化
在传统 C++ 中,变量的声明虽然能够位于任何位置,甚至于 `for` 语句内能够声明一个临时变量 `int`,但始终没有办法在 `if` 和 `switch` 语句中声明一个临时的变量。例如:
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4};
// 在 c++17 之前
const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 2);
if (itr != vec.end()) {
*itr = 3;
}
// 需要重新定义一个新的变量
const std::vector<int>::iterator itr2 = std::find(vec.begin(), vec.end(), 3);
if (itr2 != vec.end()) {
*itr2 = 4;
}
// 将输出 1, 4, 3, 4
for (std::vector<int>::iterator element = vec.begin(); element != vec.end();
++element)
std::cout << *element << std::endl;
}
```
在上面的代码中,我们可以看到 `itr` 这一变量是定义在整个 `main()` 的作用域内的,这导致当我们需要再次遍历整个 `std::vector` 时,需要重新命名另一个变量。C++17 消除了这一限制,使得我们可以在 `if`(或 `switch`)中完成这一操作:
```cpp
// 将临时变量放到 if 语句内
if (const std::vector<int>::iterator itr = std::find(vec.begin(), vec.end(), 3);
itr != vec.end()) {
*itr = 4;
}
```
怎么样,是不是和 Go 语言很像?
### 初始化列表
初始化是一个非常重要的语言特性,最常见的就是在对象进行初始化时进行使用。
在传统 C++ 中,不同的对象有着不同的初始化方法,例如普通数组、
POD (**P**lain **O**ld **D**ata,即没有构造、析构和虚函数的类或结构体)
类型都可以使用 `{}` 进行初始化,也就是我们所说的初始化列表。
而对于类对象的初始化,要么需要通过拷贝构造、要么就需要使用 `()` 进行。
这些不同方法都针对各自对象,不能通用。例如:
```cpp
#include <iostream>
#include <vector>
class Foo {
public:
int value_a;
int value_b;
Foo(int a, int b) : value_a(a), value_b(b) {}
};
int main() {
// before C++11
int arr[3] = {1, 2, 3};
Foo foo(1, 2);
std::vector<int> vec = {1, 2, 3, 4, 5};
std::cout << "arr[0]: " << arr[0] << std::endl;
std::cout << "foo:" << foo.value_a << ", " << foo.value_b << std::endl;
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << std::endl;
}
return 0;
}
```
为解决这个问题,C++11 首先把初始化列表的概念绑定到类型上,称其为 `std::initializer_list`,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:
```cpp
#include <initializer_list>
#include <vector>
#include <iostream>
class MagicFoo {
public:
std::vector<int> vec;
MagicFoo(std::initializer_list<int> list) {
for (std::initializer_list<int>::iterator it = list.begin();
it != list.end(); ++it)
vec.push_back(*it);
}
};
int main() {
// after C++11
MagicFoo magicFoo = {1, 2, 3, 4, 5};
std::cout << "magicFoo: ";
for (std::vector<int>::iterator it = magicFoo.vec.begin();
it != magicFoo.vec.end(); ++it)
std::cout << *it << std::endl;
}
```
这种构造函数被叫做初始化列表构造函数,具有这种构造函数的类型将在初始化时被特殊关照。
初始化列表除了用在对象构造上,还能将其作为普通函数的形参,例如:
```Cpp
public:
void foo(std::initializer_list<int> list) {
for (std::initializer_list<int>::iterator it = list.begin();
it != list.end(); ++it) vec.push_back(*it);
}
magicFoo.foo({6,7,8,9});
```
其次,C++11 还提供了统一的语法来初始化任意的对象,例如:
```cpp
Foo foo2 {3, 4};
```
### 结构化绑定
结构化绑定提供了类似其他语言中提供的多返回值的功能。在容器一章中,我们会学到 C++11 新增了 `std::tuple` 容器用于构造一个元组,进而囊括多个返回值。但缺陷是,C++11/14 并没有提供一种简单的方法直接从元组中拿到并定义元组中的元素,尽管我们可以使用 `std::tie` 对元组进行拆包,但我们依然必须非常清楚这个元组包含多少个对象,各个对象是什么类型,非常麻烦。
C++17 完善了这一设定,给出的结构化绑定可以让我们写出这样的代码:
```cpp
#include <iostream>
#include <tuple>
std::tuple<int, double, std::string> f() {
return std::make_tuple(1, 2.3, "456");
}
int main() {
auto [x, y, z] = f();
std::cout << x << ", " << y << ", " << z << std::endl;
return 0;
}
```
关于 `auto` 类型推导会在 [auto 类型推导](#auto)一节中进行介绍。
## 2.3 类型推导
在传统 C 和 C++ 中,参数的类型都必须明确定义,这其实对我们快速进行编码没有任何帮助,尤其是当我们面对一大堆复杂的模板类型时,必须明确的指出变量的类型才能进行后续的编码,这不仅拖慢我们的开发效率,也让代码变得又臭又长。
C++11 引入了 `auto` 和 `decltype` 这两个关键字实现了类型推导,让编译器来操心变量的类型。这使得 C++ 也具有了和其他现代编程语言一样,某种意义上提供了无需操心变量类型的使用习惯。
### auto
`auto` 在很早以前就已经进入了 C++,但是他始终作为一个存储类型的指示符存在,与 `register` 并存。在传统 C++ 中,如果一个变量没有声明为 `register` 变量,将自动被视为一个 `auto` 变量。而随着 `register` 被弃用(在 C++17 中作为保留关键字,以后使用,目前不具备实际意义),对 `auto` 的语义变更也就非常自然了。
使用 `auto` 进行类型推导的一个最为常见而且显著的例子就是迭代器。你应该在前面的小节里看到了传统 C++ 中冗长的迭代写法:
```cpp
// 在 C++11 之前
// 由于 cbegin() 将返回 vector<int>::const_iterator
// 所以 it 也应该是 vector<int>::const_iterator 类型
for(vector<int>::const_iterator it = vec.cbegin(); it != vec.cend(); ++it)
```
而有了 `auto` 之后可以:
```cpp
#include <initializer_list>
#include <vector>
#include <iostream>
class MagicFoo {
public:
std::vector<int> vec;
MagicFoo(std::initializer_list<int> list) {
// 从 C++11 起, 使用 auto 关键字进行类型推导
for (auto it = list.begin(); it != list.end(); ++it) {
vec.push_back(*it);
}
}
};
int main() {
MagicFoo magicFoo = {1, 2, 3, 4, 5};
std::cout << "magicFoo: ";
for (auto it = magicFoo.vec.begin(); it != magicFoo.vec.end(); ++it) {
std::cout << *it << ", ";
}
std::cout << std::endl;
return 0;
}
```
一些其他的常见用法:
```cpp
auto i = 5; // i 被推导为 int
auto arr = new auto(10); // arr 被推导为 int *
```
从 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
> auto auto_arr2[10] = {arr}; // 错误, 无法推导数组元素类型
>
> 2.6.auto.cpp:30:19: error: 'auto_arr2' declared as array of 'auto'
> auto auto_arr2[10] = {arr};
> ```
### decltype
`decltype` 关键字是为了解决 `auto` 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 `typeof` 很相似:
```cpp
decltype(表达式)
```
有时候,我们可能需要计算某个表达式的类型,例如:
```cpp
auto x = 1;
auto y = 2;
decltype(x+y) z;
```
你已经在前面的例子中看到 `decltype` 用于推断类型的用法,下面这个例子就是判断上面的变量 `x, y, z` 是否是同一类型:
```cpp
if (std::is_same<decltype(x), int>::value)
std::cout << "type x == int" << std::endl;
if (std::is_same<decltype(x), float>::value)
std::cout << "type x == float" << std::endl;
if (std::is_same<decltype(x), decltype(z)>::value)
std::cout << "type z == type x" << std::endl;
```
其中,`std::is_same<T, U>` 用于判断 `T` 和 `U` 这两个类型是否相等。输出结果为:
```
type x == int
type z == type x
```
### 尾返回类型推导
你可能会思考, `auto` 能不能用于推导函数的返回类型呢?还是考虑一个加法函数的例子,在传统 C++ 中我们必须这么写:
```cpp
template<typename R, typename T, typename U>
R add(T x, U y) {
return x+y;
}
```
> 注意:typename 和 class 在模板参数列表中没有区别,在 typename 这个关键字出现之前,都是使用 class 来定义模板参数的。但在模板中定义有[嵌套依赖类型](https://en.cppreference.com/w/cpp/language/dependent_name#The_typename_disambiguator_for_dependent_names)的变量时,需要用 typename 消除歧义
这样的代码其实变得很丑陋,因为程序员在使用这个模板函数的时候,必须明确指出返回类型。但事实上我们并不知道 `add()` 这个函数会做什么样的操作,以及获得一个什么样的返回类型。
在 C++11 中这个问题得到解决。虽然你可能马上会反应出来使用 `decltype` 推导 `x+y` 的类型,写出这样的代码:
```cpp
decltype(x+y) add(T x, U y)
```
但事实上这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,`x` 和 `y` 尚未被定义。为了解决这个问题,C++11 还引入了一个叫做尾返回类型(trailing return type),利用 `auto` 关键字将返回类型后置:
```cpp
template<typename T, typename U>
auto add2(T x, U y) -> decltype(x+y){
return x + y;
}
```
令人欣慰的是从 C++14 开始是可以直接让普通函数具备返回值推导,因此下面的写法变得合法:
```cpp
template<typename T, typename U>
auto add3(T x, U y){
return x + y;
}
```
可以检查一下类型推导是否正确:
```cpp
// after c++11
auto w = add2<int, double>(1, 2.0);
if (std::is_same<decltype(w), double>::value) {
std::cout << "w is double: ";
}
std::cout << w << std::endl;
// after c++14
auto q = add3<double, int>(1.0, 2);
std::cout << "q: " << q << std::endl;
```
### decltype(auto)
`decltype(auto)` 是 C++14 开始提供的一个略微复杂的用法。
> 要理解它你需要知道 C++ 中参数转发的概念,我们会在[语言运行时强化](./03-runtime.md)一章中详细介绍,你可以到时再回来看这一小节的内容。
简单来说,`decltype(auto)` 主要用于对转发函数或封装的返回类型进行推导,它使我们无需显式的指定 `decltype` 的参数表达式。考虑看下面的例子,当我们需要对下面两个函数进行封装时:
```cpp
std::string lookup1();
std::string& lookup2();
```
在 C++11 中,封装实现是如下形式:
```cpp
std::string look_up_a_string_1() {
return lookup1();
}
std::string& look_up_a_string_2() {
return lookup2();
}
```
而有了 `decltype(auto)`,我们可以让编译器完成这一件烦人的参数转发:
```cpp
decltype(auto) look_up_a_string_1() {
return lookup1();
}
decltype(auto) look_up_a_string_2() {
return lookup2();
}
```
## 2.4 控制流
### if constexpr
正如本章开头出,我们知道了 C++11 引入了 `constexpr` 关键字,它将表达式或函数编译为常量结果。一个很自然的想法是,如果我们把这一特性引入到条件判断中去,让代码在编译时就完成分支判断,岂不是能让程序效率更高?C++17 将 `constexpr` 这个关键字引入到 `if` 语句中,允许在代码中声明常量表达式的判断条件,考虑下面的代码:
```cpp
#include <iostream>
template<typename T>
auto print_type_info(const T& t) {
if constexpr (std::is_integral<T>::value) {
return t + 1;
} else {
return t + 0.001;
}
}
int main() {
std::cout << print_type_info(5) << std::endl;
std::cout << print_type_info(3.14) << std::endl;
}
```
在编译时,实际代码就会表现为如下:
```cpp
int print_type_info(const int& t) {
return t + 1;
}
double print_type_info(const double& t) {
return t + 0.001;
}
int main() {
std::cout << print_type_info(5) << std::endl;
std::cout << print_type_info(3.14) << std::endl;
}
```
### 区间 for 迭代
终于,C++11 引入了基于范围的迭代写法,我们拥有了能够写出像 Python 一样简洁的循环语句,我们可以进一步简化前面的例子:
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4};
if (auto itr = std::find(vec.begin(), vec.end(), 3); itr != vec.end()) *itr = 4;
for (auto element : vec)
std::cout << element << std::endl; // read only
for (auto &element : vec) {
element += 1; // writeable
}
for (auto element : vec)
std::cout << element << std::endl; // read only
}
```
## 2.5 模板
C++ 的模板一直是这门语言的一种特殊的艺术,模板甚至可以独立作为一门新的语言来进行使用。模板的哲学在于将一切能够在编译期处理的问题丢到编译期进行处理,仅在运行时处理那些最核心的动态服务,进而大幅优化运行期的性能。因此模板也被很多人视作 C++ 的黑魔法之一。
### 外部模板
传统 C++ 中,模板只有在使用时才会被编译器实例化。换句话说,只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要触发模板的实例化。
为此,C++11 引入了外部模板,扩充了原来的强制编译器在特定位置实例化模板的语法,使我们能够显式的通知编译器何时进行模板的实例化:
```cpp
template class std::vector<bool>; // 强行实例化
extern template class std::vector<double>; // 不在该当前编译文件中实例化模板
```
### 尖括号 ">"
在传统 C++ 的编译器中,`>>`一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码:
```cpp
std::vector<std::vector<int>> matrix;
```
这在传统 C++ 编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于像下面这种写法都能够通过编译:
```cpp
template<bool T>
class MagicType {
bool magic = T;
};
// in main function:
std::vector<MagicType<(1>2)>> magic; // 合法, 但不建议写出这样的代码
```
### 类型别名模板
在了解类型别名模板之前,需要理解『模板』和『类型』之间的不同。仔细体会这句话:**模板是用来产生类型的。**在传统 C++ 中,`typedef` 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。例如:
```cpp
template<typename T, typename U>
class MagicType {
public:
T dark;
U magic;
};
// 不合法
template<typename T>
typedef MagicType<std::vector<T>, std::string> FakeDarkMagic;
```
C++11 使用 `using` 引入了下面这种形式的写法,并且同时支持对传统 `typedef` 相同的功效:
> 通常我们使用 `typedef` 定义别名的语法是:`typedef 原名称 新名称;`,但是对函数指针等别名的定义语法却不相同,这通常给直接阅读造成了一定程度的困难。
```cpp
typedef int (*process)(void *);
using NewProcess = int(*)(void *);
template<typename T>
using TrueDarkMagic = MagicType<std::vector<T>, std::string>;
int main() {
TrueDarkMagic<bool> you;
}
```
### 变长参数模板
模板一直是 C++ 所独有的**黑魔法**(一起念:**Dark Magic**)之一。
在 C++11 之前,无论是类模板还是函数模板,都只能按其指定的样子,
接受一组固定数量的模板参数;而 C++11 加入了新的表示方法,
允许任意个数、任意类别的模板参数,同时也不需要在定义时将参数的个数固定。
```cpp
template<typename... Ts> class Magic;
```
模板类 Magic 的对象,能够接受不受限制个数的 typename 作为模板的形式参数,例如下面的定义:
```cpp
class Magic<int,
std::vector<int>,
std::map<std::string,
std::vector<int>>> darkMagic;
```
既然是任意形式,所以个数为 `0` 的模板参数也是可以的:`class Magic<> nothing;`。
如果不希望产生的模板参数个数为 `0`,可以手动的定义至少一个模板参数:
```cpp
template<typename Require, typename... Args> class Magic;
```
变长参数模板也能被直接调整到到模板函数上。传统 C 中的 `printf` 函数,
虽然也能达成不定个数的形参的调用,但其并非类别安全。
而 C++11 除了能定义类别安全的变长参数函数外,
还可以使类似 `printf` 的函数能自然地处理非自带类别的对象。
除了在模板参数中能使用 `...` 表示不定长模板参数外,
函数参数也使用同样的表示法代表不定长参数,
这也就为我们简单编写变长参数函数提供了便捷的手段,例如:
```cpp
template<typename... Args> void printf(const std::string &str, Args... args);
```
那么我们定义了变长的模板参数,如何对参数进行解包呢?
首先,我们可以使用 `sizeof...` 来计算参数的个数,:
```cpp
template<typename... Ts>
void magic(Ts... args) {
std::cout << sizeof...(args) << std::endl;
}
```
我们可以传递任意个参数给 `magic` 函数:
```cpp
magic(); // 输出0
magic(1); // 输出1
magic(1, ""); // 输出2
```
其次,对参数进行解包,到目前为止还没有一种简单的方法能够处理参数包,但有两种经典的处理手法:
**1. 递归模板函数**
递归是非常容易想到的一种手段,也是最经典的处理方法。这种方法不断递归地向函数传递模板参数,进而达到递归遍历所有模板参数的目的:
```cpp
#include <iostream>
template<typename T0>
void printf1(T0 value) {
std::cout << value << std::endl;
}
template<typename T, typename... Ts>
void printf1(T value, Ts... args) {
std::cout << value << std::endl;
printf1(args...);
}
int main() {
printf1(1, 2, "123", 1.1);
return 0;
}
```
**2. 变参模板展开**
你应该感受到了这很繁琐,在 C++17 中增加了变参模板展开的支持,于是你可以在一个函数中完成 `printf` 的编写:
```cpp
template<typename T0, typename... T>
void printf2(T0 t0, T... t) {
std::cout << t0 << std::endl;
if constexpr (sizeof...(t) > 0) printf2(t...);
}
```
> 事实上,有时候我们虽然使用了变参模板,却不一定需要对参数做逐个遍历,我们可以利用 `std::bind` 及完美转发等特性实现对函数和参数的绑定,从而达到成功调用的目的。
**3. 初始化列表展开**
递归模板函数是一种标准的做法,但缺点显而易见的在于必须定义一个终止递归的函数。
这里介绍一种使用初始化列表展开的黑魔法:
```cpp
template<typename T, typename... Ts>
auto printf3(T value, Ts... args) {
std::cout << value << std::endl;
(void) std::initializer_list<T>{([&args] {
std::cout << args << std::endl;
}(), value)...};
}
```
在这个代码中,额外使用了 C++11 中提供的初始化列表以及 Lambda 表达式的特性(下一节中将提到)。
通过初始化列表,`(lambda 表达式, value)...` 将会被展开。由于逗号表达式的出现,首先会执行前面的 lambda 表达式,完成参数的输出。
为了避免编译器警告,我们可以将 `std::initializer_list` 显式的转为 `void`。
### 折叠表达式
C++ 17 中将变长参数这种特性进一步带给了表达式,考虑下面这个例子:
```cpp
#include <iostream>
template<typename ... T>
auto sum(T ... t) {
return (t + ...);
}
int main() {
std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
}
```
### 非类型模板参数推导
前面我们主要提及的是模板参数的一种形式:类型模板参数。
```cpp
template <typename T, typename U>
auto add(T t, U u) {
return t+u;
}
```
其中模板的参数 `T` 和 `U` 为具体的类型。
但还有一种常见模板参数形式可以让不同字面量成为模板参数,即非类型模板参数:
```cpp
template <typename T, int BufSize>
class buffer_t {
public:
T& alloc();
void free(T& item);
private:
T data[BufSize];
}
buffer_t<int, 100> buf; // 100 作为模板参数
```
在这种模板参数形式下,我们可以将 `100` 作为模板的参数进行传递。
在 C++11 引入了类型推导这一特性后,我们会很自然的问,既然此处的模板参数
以具体的字面量进行传递,能否让编译器辅助我们进行类型推导,
通过使用占位符 `auto` 从而不再需要明确指明类型?
幸运的是,C++17 引入了这一特性,我们的确可以 `auto` 关键字,让编译器辅助完成具体类型的推导,
例如:
```cpp
template <auto value> void foo() {
std::cout << value << std::endl;
return;
}
int main() {
foo<10>(); // value 被推导为 int 类型
}
```
## 2.6 面向对象
### 委托构造
C++11 引入了委托构造的概念,这使得构造函数可以在同一个类中一个构造函数调用另一个构造函数,从而达到简化代码的目的:
```cpp
#include <iostream>
class Base {
public:
int value1;
int value2;
Base() {
value1 = 1;
}
Base(int value) : Base() { // 委托 Base() 构造函数
value2 = value;
}
};
int main() {
Base b(2);
std::cout << b.value1 << std::endl;
std::cout << b.value2 << std::endl;
}
```
### 继承构造
在传统 C++ 中,构造函数如果需要继承是需要将参数一一传递的,这将导致效率低下。C++11 利用关键字 `using` 引入了继承构造函数的概念:
```cpp
#include <iostream>
class Base {
public:
int value1;
int value2;
Base() {
value1 = 1;
}
Base(int value) : Base() { // 委托 Base() 构造函数
value2 = value;
}
};
class Subclass : public Base {
public:
using Base::Base; // 继承构造
};
int main() {
Subclass s(3);
std::cout << s.value1 << std::endl;
std::cout << s.value2 << std::endl;
}
```
### 显式虚函数重载
在传统 C++ 中,经常容易发生意外重载虚函数的事情。例如:
```cpp
struct Base {
virtual void foo();
};
struct SubClass: Base {
void foo();
};
```
`SubClass::foo` 可能并不是程序员尝试重载虚函数,只是恰好加入了一个具有相同名字的函数。另一个可能的情形是,当基类的虚函数被删除后,子类拥有旧的函数就不再重载该虚拟函数并摇身一变成为了一个普通的类方法,这将造成灾难性的后果。
C++11 引入了 `override` 和 `final` 这两个关键字来防止上述情形的发生。
#### override
当重载虚函数时,引入 `override` 关键字将显式的告知编译器进行重载,编译器将检查基函数是否存在这样的其函数签名一致的虚函数,否则将无法通过编译:
```cpp
struct Base {
virtual void foo(int);
};
struct SubClass: Base {
virtual void foo(int) override; // 合法
virtual void foo(float) override; // 非法, 父类没有此虚函数
};
```
#### final
`final` 则是为了防止类被继续继承以及终止虚函数继续重载引入的。
```cpp
struct Base {
virtual void foo() final;
};
struct SubClass1 final: Base {
}; // 合法
struct SubClass2 : SubClass1 {
}; // 非法, SubClass1 已 final
struct SubClass3: Base {
void foo(); // 非法, foo 已 final
};
```
### 显式禁用默认函数
在传统 C++ 中,如果程序员没有提供,编译器会默认为对象生成默认构造函数、
复制构造、赋值算符以及析构函数。
另外,C++ 也为所有类定义了诸如 `new` `delete` 这样的运算符。
当程序员有需要时,可以重载这部分函数。
这就引发了一些需求:无法精确控制默认函数的生成行为。
例如禁止类的拷贝时,必须将复制构造函数与赋值算符声明为 `private`。
尝试使用这些未定义的函数将导致编译或链接错误,则是一种非常不优雅的方式。
并且,编译器产生的默认构造函数与用户定义的构造函数无法同时存在。
若用户定义
gitextract_8bonjra0/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── Bug_report.md
│ │ ├── Feature_request.md
│ │ └── Question.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ └── website.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README-zh-cn.md
├── README.md
├── assets/
│ ├── cover-2nd-en.afphoto
│ ├── cover-2nd.afphoto
│ ├── cover-2nd.psd
│ └── donate.md
├── book/
│ ├── en-us/
│ │ ├── 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
│ │ └── toc.md
│ └── zh-cn/
│ ├── 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
│ ├── appendix3.md
│ └── toc.md
├── code/
│ ├── 1/
│ │ ├── 1.1.c.and.cpp
│ │ ├── Makefile
│ │ ├── foo.c
│ │ └── foo.h
│ ├── 10/
│ │ ├── 10.1.without.concepts.cpp
│ │ ├── 10.2.concepts.cpp
│ │ └── Makefile
│ ├── 2/
│ │ ├── 2.01.nullptr.cpp
│ │ ├── 2.02.constexpr.cpp
│ │ ├── 2.03.if.switch.cpp
│ │ ├── 2.04.initializer.list.cpp
│ │ ├── 2.05.structured.binding.cpp
│ │ ├── 2.06.auto.cpp
│ │ ├── 2.07.decltype.cpp
│ │ ├── 2.08.tail.return.type.cpp
│ │ ├── 2.09.decltype.auto.cpp
│ │ ├── 2.10.if.constexpr.cpp
│ │ ├── 2.11.for.loop.cpp
│ │ ├── 2.12.external.template.cpp
│ │ ├── 2.13.alias.template.cpp
│ │ ├── 2.14.default.template.param.cpp
│ │ ├── 2.15.variadic.template.param.cpp
│ │ ├── 2.16.fold.expression.cpp
│ │ ├── 2.18.non.type.template.auto.cpp
│ │ ├── 2.19.delegate.constructor.cpp
│ │ ├── 2.20.strong.type.enum.cpp
│ │ └── Makefile
│ ├── 3/
│ │ ├── 3.1.lambda.basic.cpp
│ │ ├── 3.2.function.wrap.cpp
│ │ ├── 3.3.rvalue.cpp
│ │ ├── 3.4.historical.cpp
│ │ ├── 3.5.move.semantics.cpp
│ │ ├── 3.6.move.semantics.cpp
│ │ ├── 3.7.perfect.forward.cpp
│ │ └── Makefile
│ ├── 4/
│ │ ├── 4.1.linear.container.cpp
│ │ ├── 4.2.unordered.map.cpp
│ │ ├── 4.3.tuples.cpp
│ │ └── Makefile
│ ├── 5/
│ │ ├── 5.1.shared.ptr.a.cpp
│ │ ├── 5.2.unique.ptr.cpp
│ │ ├── 5.3.weak.ptr.cpp
│ │ └── Makefile
│ ├── 6/
│ │ ├── 6.1.regex.cpp
│ │ └── Makefile
│ ├── 7/
│ │ ├── 7.1.thread.basic.cpp
│ │ ├── 7.2.critical.section.a.cpp
│ │ ├── 7.3.critical.section.b.cpp
│ │ ├── 7.4.futures.cpp
│ │ ├── 7.5.producer.consumer.cpp
│ │ ├── 7.6.atomic.cpp
│ │ ├── 7.6.bad.example.cpp
│ │ ├── 7.7.is.lock.free.cpp
│ │ ├── 7.8.memory.order.cpp
│ │ └── Makefile
│ └── 9/
│ ├── 9.1.noexcept.cpp
│ ├── 9.2.literals.cpp
│ ├── 9.3.alignment.cpp
│ └── Makefile
├── docker/
│ └── Dockerfile
├── epub/
│ ├── en-us/
│ │ ├── Makefile
│ │ └── filter.py
│ └── zh-cn/
│ ├── Makefile
│ └── filter.py
├── exercises/
│ ├── 2/
│ │ ├── fold.expresion.cpp
│ │ └── structured.binding.cpp
│ ├── 6/
│ │ ├── Makefile
│ │ ├── handler.hpp
│ │ ├── main.http.cpp
│ │ ├── main.https.cpp
│ │ ├── server.base.hpp
│ │ ├── server.http.hpp
│ │ ├── server.https.hpp
│ │ └── www/
│ │ ├── index.html
│ │ └── test.html
│ └── 7/
│ ├── 7.1/
│ │ ├── Makefile
│ │ ├── main.cpp
│ │ └── thread_pool.hpp
│ ├── 7.2.mutex.cpp
│ └── Makefile
├── pdf/
│ ├── en-us/
│ │ ├── Makefile
│ │ ├── aggregator.py
│ │ └── meta/
│ │ └── template.tex
│ └── zh-cn/
│ ├── Makefile
│ ├── aggregator.py
│ └── meta/
│ └── template.tex
└── website/
├── Makefile
├── README.md
├── _config.yml
├── filter.py
├── install.js
├── package.json
├── src/
│ ├── _posts/
│ │ └── index.md
│ └── modern-cpp/
│ └── about/
│ ├── ack.md
│ └── copyright.md
└── themes/
└── moderncpp/
├── _config.yml
├── layout/
│ ├── index.ejs
│ ├── layout.ejs
│ ├── page.ejs
│ ├── partials/
│ │ ├── header.ejs
│ │ ├── main_menu.ejs
│ │ ├── main_menu_en.ejs
│ │ ├── sidebar.ejs
│ │ └── toc.ejs
│ └── post.ejs
└── source/
└── modern-cpp/
├── css/
│ ├── _animations.styl
│ ├── _common.styl
│ ├── _header.styl
│ ├── _settings.styl
│ ├── _sidebar.styl
│ ├── _syntax.styl
│ ├── index.styl
│ └── page.styl
└── js/
└── common.js
SYMBOL INDEX (178 symbols across 61 files)
FILE: code/1/1.1.c.and.cpp
function main (line 15) | int main() {
FILE: code/1/foo.c
function add (line 14) | int add(int x, int y) {
FILE: code/10/10.1.without.concepts.cpp
function main (line 14) | int main() {
FILE: code/10/10.2.concepts.cpp
type Person (line 28) | struct Person {
method Person (line 30) | Person(double a, double b) : height(a), weight(b) {}
method string (line 31) | string to_string(){
type std (line 37) | namespace std {
function string (line 39) | string to_string(list<int> l){
function string (line 49) | string to_string(std::vector<int> v){
function print (line 58) | void print(Stringable a){
function print (line 62) | void print(HasStringFunc a){
function main (line 66) | int main() {
FILE: code/2/2.01.nullptr.cpp
function main (line 16) | int main() {
function foo (line 30) | void foo(char *) {
function foo (line 33) | void foo(int i) {
FILE: code/2/2.02.constexpr.cpp
function len_foo (line 13) | int len_foo() {
function len_foo_constexpr (line 17) | constexpr int len_foo_constexpr() {
function fibonacci (line 29) | constexpr int fibonacci(const int n) {
function main (line 34) | int main() {
FILE: code/2/2.03.if.switch.cpp
function main (line 14) | int main() {
FILE: code/2/2.04.initializer.list.cpp
class Foo (line 14) | class Foo {
method Foo (line 18) | Foo(int a, int b) : value_a(a), value_b(b) {}
class MagicFoo (line 21) | class MagicFoo {
method MagicFoo (line 24) | MagicFoo(std::initializer_list<int> list) {
method foo (line 29) | void foo(std::initializer_list<int> list) {
function main (line 36) | int main() {
FILE: code/2/2.05.structured.binding.cpp
function f (line 14) | std::tuple<int, double, std::string> f() {
function main (line 18) | int main() {
FILE: code/2/2.06.auto.cpp
class MagicFoo (line 14) | class MagicFoo {
method MagicFoo (line 17) | MagicFoo(std::initializer_list<int> list) {
function add (line 24) | int add(auto x, auto y) { // Supported in C++20
function main (line 28) | int main() {
FILE: code/2/2.07.decltype.cpp
function main (line 13) | int main() {
FILE: code/2/2.08.tail.return.type.cpp
function R (line 15) | R add(T x, U y) {
function add2 (line 20) | auto add2(T x, U y) -> decltype(x+y){
function add3 (line 25) | auto add3(T x, U y){
function main (line 29) | int main() {
FILE: code/2/2.09.decltype.auto.cpp
type Int (line 11) | struct Int {}
function iter (line 16) | constexpr auto iter(Int<i>) {
function main (line 20) | int main() {
FILE: code/2/2.10.if.constexpr.cpp
function print_type_info (line 13) | auto print_type_info(const T& t) {
function main (line 29) | int main() {
FILE: code/2/2.11.for.loop.cpp
function main (line 14) | int main() {
FILE: code/2/2.12.external.template.cpp
class std::vector<bool> (line 13) | class std::vector<bool>
class std::vector<double> (line 14) | class std::vector<double>
class MagicType (line 16) | class MagicType {
function main (line 20) | int main() {
FILE: code/2/2.13.alias.template.cpp
class MagicType (line 15) | class MagicType {
function main (line 30) | int main() {
FILE: code/2/2.14.default.template.param.cpp
function add (line 13) | auto add(T x, U y) -> decltype(x+y) {
function main (line 17) | int main() {
FILE: code/2/2.15.variadic.template.param.cpp
function magic (line 16) | void magic(Ts... args) {
function printf1 (line 23) | void printf1(T0 value) {
function printf1 (line 27) | void printf1(T value, Ts... args) {
function printf2 (line 34) | void printf2(T0 t0, T... t) {
function printf3 (line 41) | auto printf3(T value, Ts... args) {
function main (line 48) | int main() {
FILE: code/2/2.16.fold.expression.cpp
function sum (line 12) | auto sum(T ... t) {
function main (line 15) | int main() {
FILE: code/2/2.18.non.type.template.auto.cpp
function foo (line 12) | void foo() {
function main (line 17) | int main() {
FILE: code/2/2.19.delegate.constructor.cpp
class Base (line 12) | class Base {
method Base (line 16) | Base() = delete;
method Base (line 17) | Base(std::string s) {
method Base (line 22) | Base(std::string s, int v) : Base(s) {
method foo (line 27) | virtual void foo() final {
method foo (line 30) | virtual void foo(int v) {
class Subclass (line 34) | class Subclass final : public Base {
method Subclass (line 37) | Subclass() = delete;
method Subclass (line 40) | Subclass(double f, int v, std::string s) : Base(s, v) {
method foo (line 45) | virtual void foo(int v) override {
function main (line 57) | int main() {
FILE: code/2/2.20.strong.type.enum.cpp
type Left (line 17) | enum Left {
type Right (line 21) | enum Right {
type new_enum (line 26) | enum class new_enum : unsigned int{
function main (line 33) | int main() {
FILE: code/3/3.1.lambda.basic.cpp
function lambda_value_capture (line 15) | void lambda_value_capture() {
function lambda_reference_capture (line 27) | void lambda_reference_capture() {
function lambda_expression_capture (line 39) | void lambda_expression_capture() {
function lambda_generic (line 47) | void lambda_generic() {
function main (line 56) | int main() {
FILE: code/3/3.2.function.wrap.cpp
function functional (line 14) | void functional(foo f) {
function foo2 (line 18) | int foo2(int para) {
function foo3 (line 22) | int foo3(int a, int b, int c) {
function main (line 26) | int main() {
FILE: code/3/3.3.rvalue.cpp
function reference (line 13) | void reference(std::string& str) {
function reference (line 16) | void reference(std::string&& str) {
function main (line 20) | int main()
FILE: code/3/3.4.historical.cpp
function main (line 11) | int main() {
FILE: code/3/3.5.move.semantics.cpp
class A (line 10) | class A {
method A (line 13) | A():pointer(new int(1)) {
method A (line 16) | A(A& a):pointer(new int(*a.pointer)) {
method A (line 19) | A(A&& a):pointer(a.pointer) {
function A (line 29) | A return_rvalue(bool test) {
method A (line 13) | A():pointer(new int(1)) {
method A (line 16) | A(A& a):pointer(new int(*a.pointer)) {
method A (line 19) | A(A&& a):pointer(a.pointer) {
function main (line 34) | int main() {
FILE: code/3/3.6.move.semantics.cpp
function main (line 14) | int main() {
FILE: code/3/3.7.perfect.forward.cpp
function reference (line 11) | void reference(int& v) {
function reference (line 14) | void reference(int&& v) {
function pass (line 18) | void pass(T&& v) {
function main (line 28) | int main() {
FILE: code/4/4.1.linear.container.cpp
function foo (line 13) | void foo(int *p, int len) {
function main (line 19) | int main() {
FILE: code/4/4.2.unordered.map.cpp
function main (line 15) | int main() {
FILE: code/4/4.3.tuples.cpp
function get_student (line 15) | auto get_student(int id)
function _tuple_index (line 28) | constexpr std::variant<T...> _tuple_index(const std::tuple<T...>& tpl, s...
function tuple_index (line 36) | constexpr std::variant<T...> tuple_index(const std::tuple<T...>& tpl, si...
function tuple_len (line 41) | auto tuple_len(T &tpl) {
function main (line 51) | int main()
FILE: code/5/5.1.shared.ptr.a.cpp
function foo (line 13) | void foo(std::shared_ptr<int> i)
function main (line 18) | int main()
FILE: code/5/5.2.unique.ptr.cpp
type Foo (line 13) | struct Foo {
method Foo (line 14) | Foo() { std::cout << "Foo::Foo" << std::endl; }
method foo (line 16) | void foo() { std::cout << "Foo::foo" << std::endl; }
function f (line 19) | void f(const Foo &) {
function main (line 23) | int main() {
FILE: code/5/5.3.weak.ptr.cpp
class A (line 14) | class A
class B (line 15) | class B
class A (line 17) | class A {
class B (line 24) | class B {
function main (line 31) | int main() {
FILE: code/6/6.1.regex.cpp
function main (line 14) | int main() {
FILE: code/7/7.1.thread.basic.cpp
function main (line 13) | int main() {
FILE: code/7/7.2.critical.section.a.cpp
function critical_section (line 16) | void critical_section(int change_v) {
function main (line 26) | int main() {
FILE: code/7/7.3.critical.section.b.cpp
function critical_section (line 16) | void critical_section(int change_v) {
function main (line 35) | int main() {
FILE: code/7/7.4.futures.cpp
function main (line 14) | int main() {
FILE: code/7/7.5.producer.consumer.cpp
function main (line 18) | int main() {
FILE: code/7/7.6.atomic.cpp
function main (line 16) | int main() {
FILE: code/7/7.6.bad.example.cpp
function main (line 13) | int main() {
FILE: code/7/7.7.is.lock.free.cpp
type A (line 13) | struct A {
function main (line 19) | int main() {
FILE: code/7/7.8.memory.order.cpp
function relaxed_order (line 20) | void relaxed_order() {
function release_consume_order (line 39) | void release_consume_order() {
function release_acquire_order (line 60) | void release_acquire_order() {
function sequential_consistent_order (line 86) | void sequential_consistent_order() {
function main (line 105) | int main() {
FILE: code/9/9.1.noexcept.cpp
function may_throw (line 12) | void may_throw() {
function no_throw (line 18) | void no_throw() noexcept {
function main (line 26) | int main()
FILE: code/9/9.2.literals.cpp
function main (line 21) | int main() {
FILE: code/9/9.3.alignment.cpp
type Storage (line 13) | struct Storage {
type AlignasStorage (line 20) | struct alignas(std::max_align_t) AlignasStorage {
function main (line 27) | int main() {
FILE: exercises/2/fold.expresion.cpp
function average (line 13) | auto average(T ... t) {
function main (line 16) | int main() {
FILE: exercises/2/structured.binding.cpp
function update (line 17) | void update(std::map<Key, Value>& m, F foo) {
function main (line 21) | int main() {
FILE: exercises/6/handler.hpp
function start_server (line 15) | void start_server(SERVER_TYPE &server) {
FILE: exercises/6/main.http.cpp
function main (line 14) | int main() {
FILE: exercises/6/main.https.cpp
function main (line 12) | int main() {
FILE: exercises/6/server.base.hpp
type Web (line 17) | namespace Web {
type Request (line 18) | struct Request {
class ServerBase (line 35) | class ServerBase {
method ServerBase (line 41) | ServerBase(unsigned short port, size_t num_threads = 1) :
method start (line 46) | void start() {
method accept (line 91) | virtual void accept() {}
method process_request_and_respond (line 93) | void process_request_and_respond(std::shared_ptr<socket_type> socket...
method Request (line 134) | Request parse_request(std::istream& stream) const {
method respond (line 166) | void respond(std::shared_ptr<socket_type> socket, std::shared_ptr<Re...
class Server (line 196) | class Server : public ServerBase<socket_type> {}
FILE: exercises/6/server.http.hpp
type Web (line 13) | namespace Web {
class Server<HTTP> (line 16) | class Server<HTTP> : public ServerBase<HTTP> {
method Server (line 20) | Server(unsigned short port, size_t num_threads=1) :
method accept (line 24) | void accept() {
FILE: exercises/6/server.https.hpp
type Web (line 14) | namespace Web {
class Server<HTTPS> (line 21) | class Server<HTTPS> : public ServerBase<HTTPS> {
method Server (line 24) | Server(unsigned short port, size_t num_threads,
method accept (line 42) | void accept() {
FILE: exercises/7/7.1/main.cpp
function main (line 21) | int main()
FILE: exercises/7/7.1/thread_pool.hpp
class ThreadPool (line 27) | class ThreadPool {
FILE: exercises/7/7.2.mutex.cpp
class mutex (line 5) | class mutex {
method lock (line 9) | void lock()
method unlock (line 15) | void unlock()
function main (line 24) | int main() {
FILE: website/themes/moderncpp/source/modern-cpp/js/common.js
function initMobileMenu (line 13) | function initMobileMenu () {
function initVersionSelect (line 54) | function initVersionSelect () {
function initSubHeaders (line 74) | function initSubHeaders () {
Condensed preview — 160 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (425K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 722,
"preview": "# These are supported funding model platforms\n\ngithub: [changkun] # Replace with up to 4 GitHub Sponsors-enabled usernam"
},
{
"path": ".github/ISSUE_TEMPLATE/Bug_report.md",
"chars": 455,
"preview": "---\nname: Bug report\nabout: 报告错误\n\n---\n\n<!-- English Version -->\n\n## Actual Description\n\n- File Path: for example, `book/"
},
{
"path": ".github/ISSUE_TEMPLATE/Feature_request.md",
"chars": 263,
"preview": "---\nname: Feature request\nabout: 内容建议\n\n---\n\n<!-- English Version -->\n\n## Motivation\n\nPlease briefly describe your motiva"
},
{
"path": ".github/ISSUE_TEMPLATE/Question.md",
"chars": 388,
"preview": "---\nname: Question\nabout: 提交问题\n\n---\n\n<!-- English Version -->\n\n## Question\n\nPlease describe your question here, and read"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 451,
"preview": "resolve #issue_id\n\n<!-- English Version -->\n\n## Description\n\nPlease describe the motivation of this pull request, and wh"
},
{
"path": ".github/workflows/website.yml",
"chars": 734,
"preview": "name: Website\n\non:\n push:\n branches: [ master ]\n\njobs:\n\n build:\n name: Website\n runs-on: ubuntu-latest\n ti"
},
{
"path": ".gitignore",
"chars": 798,
"preview": "# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dyl"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3353,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 3927,
"preview": "# How to contribute\n\n## Submit Issue\n\nC++ 11/14/17 issue is used to track the principle description error, `typo` error,"
},
{
"path": "LICENSE",
"chars": 1092,
"preview": "MIT License\n\nCopyright (c) 2016 - 2020 Changkun Ou <hi@changkun.de>\n\nPermission is hereby granted, free of charge, to an"
},
{
"path": "Makefile",
"chars": 817,
"preview": "NAME=modern-cpp-tutorial\nDOCKER_ENV=changkun/$(NAME):build-env\nTARGET = pdf epub\nLANGS = zh-cn en-us\nALL_BUILDS = websit"
},
{
"path": "README-zh-cn.md",
"chars": 2825,
"preview": "<img src=\"assets/cover-2nd.png\" alt=\"logo\" height=\"550\" align=\"right\" />\n\n# 现代 C++ 教程:高速上手 C++11/14/17/20\n\n\n- [**Chapter 01 Towards Modern C++*"
},
{
"path": "book/zh-cn/00-preface.md",
"chars": 2239,
"preview": "---\ntitle: 序言\ntype: book-zh-cn\norder: 0\n---\n\n# 序言\n\n[TOC]\n\n## 引言\n\nC++ 是一个用户群体相当大的语言。从 C++98 的出现到 C++11 的正式定稿经历了长达十年多之久的积累"
},
{
"path": "book/zh-cn/01-intro.md",
"chars": 3787,
"preview": "---\ntitle: 第 1 章 迈向现代 C++\ntype: book-zh-cn\norder: 1\n---\n\n# 第 1 章 迈向现代 C++\n\n[TOC]\n\n**编译环境**:本书将使用 `clang++` 作为唯一使用的编译器,同时"
},
{
"path": "book/zh-cn/02-usability.md",
"chars": 22426,
"preview": "---\ntitle: 第 2 章 语言可用性的强化\ntype: book-zh-cn\norder: 2\n---\n\n# 第 2 章 语言可用性的强化\n\n[TOC]\n\n当我们声明、定义一个变量或者常量,对代码进行流程控制、面向对象的功能、模板编"
},
{
"path": "book/zh-cn/03-runtime.md",
"chars": 13605,
"preview": "---\ntitle: 第 3 章 语言运行期的强化\ntype: book-zh-cn\norder: 3\n---\n\n# 第 3 章 语言运行期的强化\n\n[TOC]\n\n## 3.1 Lambda 表达式\n\nLambda 表达式是现代 C++ 中"
},
{
"path": "book/zh-cn/04-containers.md",
"chars": 7402,
"preview": "---\ntitle: 第 4 章 容器\ntype: book-zh-cn\norder: 4\n---\n\n# 第 4 章 容器\n\n[TOC]\n\n## 4.1 线性容器\n\n### `std::array`\n\n看到这个容器的时候肯定会出现这样的问题"
},
{
"path": "book/zh-cn/05-pointers.md",
"chars": 5892,
"preview": "---\ntitle: 第 5 章 智能指针与内存管理\ntype: book-zh-cn\norder: 5\n---\n\n# 第 5 章 智能指针与内存管理\n\n[TOC]\n\n## 5.1 RAII 与引用计数\n\n了解 `Objective-C`/"
},
{
"path": "book/zh-cn/06-regex.md",
"chars": 6693,
"preview": "---\ntitle: 第 6 章 正则表达式\ntype: book-zh-cn\norder: 6\n---\n\n# 第 6 章 正则表达式\n\n[TOC]\n\n## 6.1 正则表达式简介\n\n正则表达式不是 C++ 语言的一部分,这里仅做简单的介绍"
},
{
"path": "book/zh-cn/07-thread.md",
"chars": 14913,
"preview": "---\ntitle: 第 7 章 并行与并发\ntype: book-zh-cn\norder: 7\n---\n\n# 第 7 章 并行与并发\n\n[TOC]\n\n## 7.1 并行基础\n\n`std::thread` 用于创建一个执行的线程实例,所以它"
},
{
"path": "book/zh-cn/08-filesystem.md",
"chars": 648,
"preview": "---\ntitle: 第 8 章 文件系统\ntype: book-zh-cn\norder: 8\n---\n\n# 第 8 章 文件系统\n\n[TOC]\n\n文件系统库提供了文件系统、路径、常规文件、目录等等相关组件进行操作的相关功能。和正则表达式库"
},
{
"path": "book/zh-cn/09-others.md",
"chars": 4076,
"preview": "---\ntitle: 第 9 章 其他杂项\ntype: book-zh-cn\norder: 9\n---\n\n# 第 9 章 其他杂项\n\n[TOC]\n\n## 9.1 新类型\n\n### `long long int`\n\n`long long in"
},
{
"path": "book/zh-cn/10-cpp20.md",
"chars": 1979,
"preview": "---\ntitle: 第 10 章 展望:C++20 简介\ntype: book-zh-cn\norder: 10\n---\n\n# 第 10 章 展望:C++20 简介\n\n[TOC]\n\nC++20 如同 C++11 一样,似乎能够成为一个振奋人"
},
{
"path": "book/zh-cn/appendix1.md",
"chars": 924,
"preview": "---\ntitle: 附录 1:进一步阅读的学习材料\ntype: book-zh-cn\norder: 11\n---\n\n# 附录 1:进一步阅读的学习材料\n\n首先,恭喜 🎉 你阅读完本书!笔者希望本书有提起你对现代 C++ 的兴趣。\n\n正如本"
},
{
"path": "book/zh-cn/appendix2.md",
"chars": 948,
"preview": "---\ntitle: 附录 2:现代 C++ 的最佳实践\ntype: book-zh-cn\norder: 12\n---\n\n# 附录 2:现代 C++ 的最佳实践\n\n这篇附录我们来简单谈一谈现代 C++ 的最佳实践。总的来说,笔者关于 C++"
},
{
"path": "book/zh-cn/appendix3.md",
"chars": 846,
"preview": "---\ntitle: 附录 3:现代 C++ 特性索引\ntype: book-zh-cn\norder: 13\n---\n\n# 附录 3:现代 C++ 特性索引表\n\n| 特性概述 | 首次出现章节 | 首次出现版本 |\n|:--------|:"
},
{
"path": "book/zh-cn/toc.md",
"chars": 2616,
"preview": "# 现代 C++ 教程:高速上手 C++ 11/14/17/20\n\n## 目录\n\n- [**序言**](./00-preface.md)\n- [**第 1 章 迈向现代 C++**](./01-intro.md)\n + 1.1 被弃用"
},
{
"path": "code/1/1.1.c.and.cpp",
"chars": 391,
"preview": "//\n// 1.1.cpp\n//\n// chapter 1 introduction\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n// https://gi"
},
{
"path": "code/1/Makefile",
"chars": 408,
"preview": "#\n# 1.1.cpp\n#\n# chapter 1 introduction\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com"
},
{
"path": "code/1/foo.c",
"chars": 230,
"preview": "//\n// foo.c\n//\n// chapter 1 introduction\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n// https://gith"
},
{
"path": "code/1/foo.h",
"chars": 260,
"preview": "//\n// foo.h\n//\n// chapter 1 introduction\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n// https://gith"
},
{
"path": "code/10/10.1.without.concepts.cpp",
"chars": 335,
"preview": "//\n// 10.1.without.concepts.cpp\n// chapter 10 cpp20\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// h"
},
{
"path": "code/10/10.2.concepts.cpp",
"chars": 1388,
"preview": "//\n// 10.2.concepts.cpp\n// chapter 10 cpp20\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://g"
},
{
"path": "code/10/Makefile",
"chars": 244,
"preview": "#\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com/changkun/modern-cpp-tutorial\n#\n\nall:"
},
{
"path": "code/2/2.01.nullptr.cpp",
"chars": 896,
"preview": "//\n// 2.1.nullptr.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n//"
},
{
"path": "code/2/2.02.constexpr.cpp",
"chars": 1205,
"preview": "//\n// 2.2.constexpr.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n"
},
{
"path": "code/2/2.03.if.switch.cpp",
"chars": 818,
"preview": "//\n// 2.3.if.switch.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n"
},
{
"path": "code/2/2.04.initializer.list.cpp",
"chars": 1577,
"preview": "//\n// 2.4.initializer.list.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at chang"
},
{
"path": "code/2/2.05.structured.binding.cpp",
"chars": 443,
"preview": "//\n// 2.5.structured.binding.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at cha"
},
{
"path": "code/2/2.06.auto.cpp",
"chars": 992,
"preview": "//\n// 2.6.auto.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n// ht"
},
{
"path": "code/2/2.07.decltype.cpp",
"chars": 612,
"preview": "//\n// 2.7.decltype.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n/"
},
{
"path": "code/2/2.08.tail.return.type.cpp",
"chars": 933,
"preview": "//\n// 2.8.tail.return.type.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at chang"
},
{
"path": "code/2/2.09.decltype.auto.cpp",
"chars": 376,
"preview": "//\n// 2.9.decltype.auto.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changkun"
},
{
"path": "code/2/2.10.if.constexpr.cpp",
"chars": 653,
"preview": "//\n// 2.10.if.constexpr.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changkun"
},
{
"path": "code/2/2.11.for.loop.cpp",
"chars": 639,
"preview": "//\n// 2.11.for.loop.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n"
},
{
"path": "code/2/2.12.external.template.cpp",
"chars": 610,
"preview": "//\n// 2.12.external.template.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at cha"
},
{
"path": "code/2/2.13.alias.template.cpp",
"chars": 652,
"preview": "//\n// 2.13.alias.template.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at changk"
},
{
"path": "code/2/2.14.default.template.param.cpp",
"chars": 366,
"preview": "//\n// 2.4.default.template.param.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at"
},
{
"path": "code/2/2.15.variadic.template.param.cpp",
"chars": 1251,
"preview": "//\n// 2.15.variadic.template.param.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun "
},
{
"path": "code/2/2.16.fold.expression.cpp",
"chars": 351,
"preview": "//\n// 2.16.fold.expression.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun at chang"
},
{
"path": "code/2/2.18.non.type.template.auto.cpp",
"chars": 358,
"preview": "//\n// 2.18.non.type.template.auto.cpp\n// chapter 2 language usability\n// modern cpp tutorial\n//\n// created by changkun a"
},
{
"path": "code/2/2.19.delegate.constructor.cpp",
"chars": 1353,
"preview": "//\n// 2.19.constructor.cpp\n// chapter 2 language usability\n// modern c++ tutorial\n//\n// created by changkun at changkun."
},
{
"path": "code/2/2.20.strong.type.enum.cpp",
"chars": 1127,
"preview": "//\n// 2.20.strong.type.enum.cpp\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://github.com/ch"
},
{
"path": "code/2/Makefile",
"chars": 244,
"preview": "#\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com/changkun/modern-cpp-tutorial\n#\n\nall:"
},
{
"path": "code/3/3.1.lambda.basic.cpp",
"chars": 1536,
"preview": "//\n// 3.1.lambda.basic.cpp\n// chapter 03 runtime enhancement\n// modern c++ tutorial\n//\n// created by changkun at changku"
},
{
"path": "code/3/3.2.function.wrap.cpp",
"chars": 1190,
"preview": "//\n// 3.2.function.wrap.cpp\n// chapter 03 runtime enhancement\n// modern c++ tutorial\n//\n// created by changkun at changk"
},
{
"path": "code/3/3.3.rvalue.cpp",
"chars": 1198,
"preview": "//\n// 3.3.rvalue.cpp\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://github.com/changkun/mode"
},
{
"path": "code/3/3.4.historical.cpp",
"chars": 370,
"preview": "//\n// 3.4.historical.cpp\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://github.com/changkun/"
},
{
"path": "code/3/3.5.move.semantics.cpp",
"chars": 1030,
"preview": "//\n// 3.5.move.semantics.cpp\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://github.com/chang"
},
{
"path": "code/3/3.6.move.semantics.cpp",
"chars": 768,
"preview": "//\n// 3.5.move.semantics.cpp\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://github.com/chang"
},
{
"path": "code/3/3.7.perfect.forward.cpp",
"chars": 888,
"preview": "//\n// 3.6.perfect.forward.cpp\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://github.com/chan"
},
{
"path": "code/3/Makefile",
"chars": 244,
"preview": "#\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com/changkun/modern-cpp-tutorial\n#\n\nall:"
},
{
"path": "code/4/4.1.linear.container.cpp",
"chars": 2290,
"preview": "//\n// 4.1.linear.container.cpp\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://github.com/cha"
},
{
"path": "code/4/4.2.unordered.map.cpp",
"chars": 879,
"preview": "//\n// 4.2.unordered.map.cpp\n// chapter 04 containers\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// "
},
{
"path": "code/4/4.3.tuples.cpp",
"chars": 2408,
"preview": "//\n// 4.3.tuples.cpp\n// chapter 04 containers\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https:/"
},
{
"path": "code/4/Makefile",
"chars": 244,
"preview": "#\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com/changkun/modern-cpp-tutorial\n#\n\nall:"
},
{
"path": "code/5/5.1.shared.ptr.a.cpp",
"chars": 1795,
"preview": "//\n// 5.1.shared.ptr.cpp\n// chapter 05 start pointers and memory management\n// modern c++ tutorial\n//\n// created by chan"
},
{
"path": "code/5/5.2.unique.ptr.cpp",
"chars": 1164,
"preview": "//\n// 5.2.unique.ptr.cpp\n// chapter 05 start pointers and memory management\n// modern c++ tutorial\n//\n// created by chan"
},
{
"path": "code/5/5.3.weak.ptr.cpp",
"chars": 672,
"preview": "//\n// 5.3.weak.ptr.cpp\n// chapter 05 start pointers and memory management\n// modern c++ tutorial\n//\n// created by changk"
},
{
"path": "code/5/Makefile",
"chars": 244,
"preview": "#\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com/changkun/modern-cpp-tutorial\n#\n\nall:"
},
{
"path": "code/6/6.1.regex.cpp",
"chars": 1352,
"preview": "//\n// 6.1.regex.cpp\n// chapter 06 regular expression\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// "
},
{
"path": "code/6/Makefile",
"chars": 244,
"preview": "#\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com/changkun/modern-cpp-tutorial\n#\n\nall:"
},
{
"path": "code/7/7.1.thread.basic.cpp",
"chars": 351,
"preview": "//\n// 7.1.thread.basic.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changkun at "
},
{
"path": "code/7/7.2.critical.section.a.cpp",
"chars": 634,
"preview": "//\n// 7.2.critical.section.a.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changk"
},
{
"path": "code/7/7.3.critical.section.b.cpp",
"chars": 823,
"preview": "//\n// 7.3.critical.section.b.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changk"
},
{
"path": "code/7/7.4.futures.cpp",
"chars": 739,
"preview": "//\n// 7.4.futures.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changkun at chang"
},
{
"path": "code/7/7.5.producer.consumer.cpp",
"chars": 1754,
"preview": "//\n// 7.5.producer.consumer.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changku"
},
{
"path": "code/7/7.6.atomic.cpp",
"chars": 558,
"preview": "//\n// 7.6.atomic.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changkun at changk"
},
{
"path": "code/7/7.6.bad.example.cpp",
"chars": 525,
"preview": "//\n// 7.6.bad.example.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changkun at c"
},
{
"path": "code/7/7.7.is.lock.free.cpp",
"chars": 398,
"preview": "//\n// 7.7.is.lock.free.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changkun at "
},
{
"path": "code/7/7.8.memory.order.cpp",
"chars": 2692,
"preview": "//\n// 7.8.memory.order.cpp\n// chapter 7 parallelism and concurrency\n// modern c++ tutorial\n//\n// created by changkun at "
},
{
"path": "code/7/Makefile",
"chars": 244,
"preview": "#\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com/changkun/modern-cpp-tutorial\n#\n\nall:"
},
{
"path": "code/9/9.1.noexcept.cpp",
"chars": 1120,
"preview": "//\n// 9.1.noexcept.cpp\n// chapter 09 others\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://g"
},
{
"path": "code/9/9.2.literals.cpp",
"chars": 748,
"preview": "//\n// 9.2.literals.cpp\n// chapter 09 others\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://g"
},
{
"path": "code/9/9.3.alignment.cpp",
"chars": 528,
"preview": "//\n// 9.3.alignment.cpp\n// chapter 09 others\n// modern c++ tutorial\n//\n// created by changkun at changkun.de\n// https://"
},
{
"path": "code/9/Makefile",
"chars": 244,
"preview": "#\n# modern cpp tutorial\n#\n# created by changkun at changkun.de\n# https://github.com/changkun/modern-cpp-tutorial\n#\n\nall:"
},
{
"path": "docker/Dockerfile",
"chars": 536,
"preview": "FROM node:latest\n\nLABEL \"maintainer\"=\"Changkun Ou <hi[at]changkun.de>\"\nLABEL \"repository\"=\"https://github.com/changkun/m"
},
{
"path": "epub/en-us/Makefile",
"chars": 1701,
"preview": "title = 'Modern C++ Tutorial: C++11/14/17/20 On the Fly'\nfilename = 'modern-cpp-tutorial'\noutputname='modern-cpp-tutoria"
},
{
"path": "epub/en-us/filter.py",
"chars": 758,
"preview": "# !/usr/bin/env python3\n# author: changkun<hi[at]changkun.de>\n\nimport os\n\nchapters = ['00-preface.md', '01-intro.md', '"
},
{
"path": "epub/zh-cn/Makefile",
"chars": 1568,
"preview": "title = '现代 C++ 教程:高速上手 C++11/14/17/20'\nfilename = 'modern-cpp-tutorial'\noutputname='modern-cpp-tutorial'\nrevision = $(s"
},
{
"path": "epub/zh-cn/filter.py",
"chars": 741,
"preview": "# !/usr/bin/env python3\n# author: changkun<hi[at]changkun.de>\n\nimport os\n\nchapters = ['00-preface.md', '01-intro.md', '"
},
{
"path": "exercises/2/fold.expresion.cpp",
"chars": 374,
"preview": "//\n// fold.expression.cpp\n//\n// exercise solution - chapter 2\n// modern cpp tutorial\n//\n// created by changkun at changk"
},
{
"path": "exercises/2/structured.binding.cpp",
"chars": 729,
"preview": "//\n// structured.binding.cpp\n//\n// exercise solution - chapter 2\n// modern cpp tutorial\n//\n// created by changkun at cha"
},
{
"path": "exercises/6/Makefile",
"chars": 880,
"preview": "#\n# Makefile\n# web_server\n#\n# created by changkun at changkun.de/modern-cpp\n#\n\nCXX = g++\nEXEC_HTTP = server.http\nEXEC_HT"
},
{
"path": "exercises/6/handler.hpp",
"chars": 3516,
"preview": "//\n// handler.hpp\n// web_server\n// created by changkun at changkun.de\n// https://github.com/changkun/modern-cpp-tutorial"
},
{
"path": "exercises/6/main.http.cpp",
"chars": 450,
"preview": "//\n// main_http.cpp\n// web_server\n// created by changkun at changkun.de\n// https://github.com/changkun/modern-cpp-tutori"
},
{
"path": "exercises/6/main.https.cpp",
"chars": 513,
"preview": "//\n// main_https.cpp\n// web_server\n// created by changkun at changkun.de\n// https://github.com/changkun/modern-cpp-tutor"
},
{
"path": "exercises/6/server.base.hpp",
"chars": 8120,
"preview": "//\n// server_base.hpp\n// web_server\n// created by changkun at changkun.de\n// https://github.com/changkun/modern-cpp-tuto"
},
{
"path": "exercises/6/server.http.hpp",
"chars": 1297,
"preview": "//\n// server_http.hpp\n// web_server\n// created by changkun at changkun.de\n// https://github.com/changkun/modern-cpp-tuto"
},
{
"path": "exercises/6/server.https.hpp",
"chars": 2428,
"preview": "//\n// server_https.hpp\n// web_server\n// created by changkun at changkun.de\n// https://github.com/changkun/modern-cpp-tut"
},
{
"path": "exercises/6/www/index.html",
"chars": 135,
"preview": "<html>\n <head>\n <title>Web Server Test</title>\n </head>\n <body>\n Hello world in index.html.\n <"
},
{
"path": "exercises/6/www/test.html",
"chars": 134,
"preview": "<html>\n <head>\n <title>Web Server Test</title>\n </head>\n <body>\n Hello world in test.html.\n </"
},
{
"path": "exercises/7/7.1/Makefile",
"chars": 254,
"preview": "#\n# Makefile\n#\n# exercise solution 7.1 - chapter 7\n# modern cpp tutorial\n#\n# created by changkun at changkun.de/modern-c"
},
{
"path": "exercises/7/7.1/main.cpp",
"chars": 1421,
"preview": "//\n// main.cpp\n//\n// exercise solution - chapter 7\n// modern cpp tutorial\n//\n// created by changkun at changkun.de\n// ht"
},
{
"path": "exercises/7/7.1/thread_pool.hpp",
"chars": 4496,
"preview": "//\n// thread_pool.hpp\n//\n// exercise solution - chapter 7\n// modern cpp tutorial\n//\n// created by changkun at changkun.d"
},
{
"path": "exercises/7/7.2.mutex.cpp",
"chars": 734,
"preview": "#include <atomic>\n#include <thread>\n#include <iostream>\n\nclass mutex {\n std::atomic<bool> flag{false};\n\npublic:\n v"
},
{
"path": "exercises/7/Makefile",
"chars": 250,
"preview": "#\n# Makefile\n#\n# exercise solution - chapter 7\n# modern cpp tutorial\n#\n# created by changkun at changkun.de/modern-cpp\n#"
},
{
"path": "pdf/en-us/Makefile",
"chars": 759,
"preview": "title = 'Modern C++ Tutorial: C++11/14/17/20 On the Fly'\nfilename = 'modern-cpp-tutorial'\noutputname='modern-cpp-tutoria"
},
{
"path": "pdf/en-us/aggregator.py",
"chars": 921,
"preview": "# !/usr/bin/env python3\n# author: changkun<hi[at]changkun.de>\n\nimport os\n\nchapters = ['00-preface.md', '01-intro.md', '"
},
{
"path": "pdf/en-us/meta/template.tex",
"chars": 5105,
"preview": "\\documentclass[a4paper, 10pt]{article}\n\\usepackage{geometry}\n\\geometry{\n top=1in,\n inner=1in,\n outer=1in,\n bottom=1i"
},
{
"path": "pdf/zh-cn/Makefile",
"chars": 742,
"preview": "title = '现代 C++ 教程:高速上手 C++11/14/17/20'\nfilename = 'modern-cpp-tutorial'\noutputname='modern-cpp-tutorial'\nrevision = $(s"
},
{
"path": "pdf/zh-cn/aggregator.py",
"chars": 915,
"preview": "# !/usr/bin/env python3\n# author: changkun<hi[at]changkun.de>\n\nimport os, io\n\nchapters = ['00-preface.md', '01-intro.md'"
},
{
"path": "pdf/zh-cn/meta/template.tex",
"chars": 5284,
"preview": "\\documentclass[a4paper, 10pt]{ctexart}\n\\usepackage{geometry}\n\\geometry{\n top=1in,\n inner=1in,\n outer=1in,\n bottom=1i"
},
{
"path": "website/Makefile",
"chars": 1222,
"preview": "all: clean\n\tnode install.js\n\tpython3 filter.py\n\tcp ../assets/cover-2nd.png ./src/modern-cpp/assets/cover-2nd.png\n\tcp ../"
},
{
"path": "website/README.md",
"chars": 424,
"preview": "# changkun.de/modern-cpp\n\nWelcome to Modern C++ Tutorial! This site is built with [hexo](http://hexo.io/). Site content "
},
{
"path": "website/_config.yml",
"chars": 1475,
"preview": "# Site Configuration\ntitle: \"现代 C++ 教程: 高速上手 C++ 11/14/17/20\"\nsubtitle: C++ 11/14/17/20 On the Fly\ndescription: \"欧长坤\"\nau"
},
{
"path": "website/filter.py",
"chars": 1201,
"preview": "# !/usr/bin/env python3\n# author: changkun<hi[at]changkun.de>\nimport os\nimport re\n\nsource_dir = [\n '../book/zh-cn/',\n"
},
{
"path": "website/install.js",
"chars": 440,
"preview": "var fs = require('fs')\nvar execSync = require('child_process').execSync\nvar deps = require('./package.json').dependencie"
},
{
"path": "website/package.json",
"chars": 390,
"preview": "{\n \"name\": \"modern-cpp\",\n \"version\": \"2.0.0\",\n \"private\": true,\n \"hexo\": {\n \"version\": \"5.3.0\"\n },\n \"dependenci"
},
{
"path": "website/src/_posts/index.md",
"chars": 21,
"preview": "---\nindex: true\n---\n\n"
},
{
"path": "website/src/modern-cpp/about/ack.md",
"chars": 1661,
"preview": "---\ntitle: 致谢\ntype: about\norder: 4\n---\n\n## Acknowledgements\n\nThis book was originally written in Chinese by [Changkun Ou"
},
{
"path": "website/src/modern-cpp/about/copyright.md",
"chars": 412,
"preview": "---\ntitle: 版权声明\ntype: about\norder: 3\n---\n\n<a rel=\"license\" href=\"https://creativecommons.org/licenses/by-nc-nd/4.0/\"><im"
},
{
"path": "website/themes/moderncpp/_config.yml",
"chars": 183,
"preview": "site_description: \"Modern C++ Tutorial | C++ 11/14/17/20 On the Fly | 现代 C++ 教程 | 高速上手 C++11/14/17/20\"\ngoogle_analytics:"
},
{
"path": "website/themes/moderncpp/layout/index.ejs",
"chars": 2423,
"preview": "<div class=\"sidebar\">\n <div class=\"sidebar-inner-index\">\n <ul class=\"main-menu\">\n <% if (page.type == 'book-en-"
},
{
"path": "website/themes/moderncpp/layout/layout.ejs",
"chars": 2913,
"preview": "<% var isIndex = page.path === 'index.html' %>\n\n<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title><%- page.title ? pa"
},
{
"path": "website/themes/moderncpp/layout/page.ejs",
"chars": 2163,
"preview": "<% if (page.type) { %>\n <%- partial('partials/sidebar', { type: page.type, index: page.index }) %>\n<% } else { %>\n <di"
},
{
"path": "website/themes/moderncpp/layout/partials/header.ejs",
"chars": 653,
"preview": "<div id=\"header\">\n <a id=\"logo\" href=\"<%- url_for(\"/modern-cpp/\") %>\">\n <% if (page.type == 'book-en-us') { %>\n "
},
{
"path": "website/themes/moderncpp/layout/partials/main_menu.ejs",
"chars": 1805,
"preview": "<li class=\"nav-dropdown-container resource\">\n <a class=\"nav-link\">资源</a><span class=\"arrow\"></span>\n <ul class=\"nav-dr"
},
{
"path": "website/themes/moderncpp/layout/partials/main_menu_en.ejs",
"chars": 1806,
"preview": "<li class=\"nav-dropdown-container resource\">\n<a class=\"nav-link\">Resources</a><span class=\"arrow\"></span>\n<ul class=\"nav"
},
{
"path": "website/themes/moderncpp/layout/partials/sidebar.ejs",
"chars": 1189,
"preview": "<div class=\"sidebar\">\n <div class=\"sidebar-inner\">\n <ul class=\"main-menu\">\n <% if (page.type == 'book-en-us') {"
},
{
"path": "website/themes/moderncpp/layout/partials/toc.ejs",
"chars": 286,
"preview": "<ul class=\"menu-root\">\n <% site.pages.find({type: type}).sort('order').each(function (p) { %>\n <li>\n <a href=\"<"
},
{
"path": "website/themes/moderncpp/layout/post.ejs",
"chars": 805,
"preview": "<div class=\"sidebar blog\">\n <div class=\"sidebar-inner\">\n <ul class=\"main-menu\">\n <% if (page.type == 'book-en"
},
{
"path": "website/themes/moderncpp/source/modern-cpp/css/_animations.styl",
"chars": 240,
"preview": ".rotating-clockwise\n animation: 3s rotating-clockwise linear infinite\n\ni.rotating-clockwise\n display: inline-block\n a"
},
{
"path": "website/themes/moderncpp/source/modern-cpp/css/_common.styl",
"chars": 3488,
"preview": "@import \"_settings\"\n@import \"_syntax\"\n\nbody\n font-family: $body-font\n font-size: $body-font-size\n -webkit-font-smooth"
},
{
"path": "website/themes/moderncpp/source/modern-cpp/css/_header.styl",
"chars": 2822,
"preview": "$header-height = 40px\n\n#header\n background-color: $theme\n height: $header-height\n padding: $heading-padding-vertical "
},
{
"path": "website/themes/moderncpp/source/modern-cpp/css/_settings.styl",
"chars": 871,
"preview": "// font faces\n$body-font = \"Source Sans Pro\", \"Helvetica Neue\", Arial, sans-serif\n$logo-font = \"Dosis\", \"Source Sans Pro"
},
{
"path": "website/themes/moderncpp/source/modern-cpp/css/_sidebar.styl",
"chars": 1902,
"preview": "@import \"_settings\"\n\n.sidebar\n position: fixed\n z-index: 10\n top: $header-height\n left: 0\n bottom: 0\n overflow-x: "
},
{
"path": "website/themes/moderncpp/source/modern-cpp/css/_syntax.styl",
"chars": 1215,
"preview": ".gutter pre\n color: #999\n\npre\n color: #525252\n .function .keyword,\n .constant\n color: #0092db\n .keyword,\n .attr"
},
{
"path": "website/themes/moderncpp/source/modern-cpp/css/index.styl",
"chars": 2621,
"preview": "@import \"_common\"\n@import \"_header\"\n@import \"_sidebar\"\n\n$width = 900px\n\nbody\n background-color: #fff\n margin-bottom: 2"
},
{
"path": "website/themes/moderncpp/source/modern-cpp/css/page.styl",
"chars": 4893,
"preview": "@import \"_common\"\n@import \"_animations\"\n@import \"_header\"\n@import \"_sidebar\"\n\n#header\n box-shadow: 0 0 1px rgba(0,0,0,."
},
{
"path": "website/themes/moderncpp/source/modern-cpp/js/common.js",
"chars": 8340,
"preview": "(function () {\n \n initMobileMenu()\n if (PAGE_TYPE) {\n initVersionSelect()\n initSubHeaders()\n }\n \n "
}
]
// ... and 3 more files (download for full content)
About this extraction
This page contains the full source code of the changkun/modern-cpp-tutorial GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 160 files (390.8 KB), approximately 128.4k tokens, and a symbol index with 178 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.