diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..2957eb0e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [changkun] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md new file mode 100644 index 00000000..2c6b76cb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -0,0 +1,49 @@ +--- +name: Bug report +about: 报告错误 + +--- + + + +## 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 +- 原文段落: + +``` +复制原文段落 +``` + +## 预期描述 + +``` +修改后的段落 +``` + +## 附图 + +必要时,请附上相关截图 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md new file mode 100644 index 00000000..470e91ba --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -0,0 +1,27 @@ +--- +name: Feature request +about: 内容建议 + +--- + + + +## Motivation + +Please briefly describe your motivation. + +## Requirements + +Please list all of your suggestions. + +--- + + + +## 动机 + +请描述你提交内容建议的动机。 + +## 需求说明 + +请描述你提交内容建议的详单,例如具体是增加哪个知识点的说明。 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/Question.md b/.github/ISSUE_TEMPLATE/Question.md new file mode 100644 index 00000000..0c13fb73 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Question.md @@ -0,0 +1,19 @@ +--- +name: Question +about: 提交问题 + +--- + + + +## 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) \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..29c3aa7b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ +resolve #issue_id + + + +## 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 的描述性错误 + +## 参考文献 + +如果有请注明 \ No newline at end of file diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml new file mode 100644 index 00000000..a9d7187d --- /dev/null +++ b/.github/workflows/website.yml @@ -0,0 +1,29 @@ +name: Website + +on: + push: + branches: [ master ] + +jobs: + + build: + name: Website + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v2 + - name: build + env: + USER: ${{ secrets.SERVER_USER }} + TARGET: ${{ secrets.SERVER_PATH }} + KEY: ${{ secrets.SERVER_KEY }} + DOMAIN: ${{ secrets.SERVER_DOMAIN }} + run: | + make build + mkdir ~/.ssh + echo "$KEY" | tr -d '\r'> ~/.ssh/id_ed25519 + chmod 400 ~/.ssh/id_ed25519 + eval "$(ssh-agent -s)" + ssh-add ~/.ssh/id_ed25519 + ssh-keyscan -H $DOMAIN>> ~/.ssh/known_hosts + scp -r website/public/modern-cpp/* $USER@$DOMAIN:$TARGET \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8f7929a2..f6224793 100644 --- a/.gitignore +++ b/.gitignore @@ -28,5 +28,23 @@ *.out *.app +.vscode + node_modules -website/public \ No newline at end of file + +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 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..eaf83cd9 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..054bc2ad --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,71 @@ +# 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` 文件夹下的内容 diff --git a/LICENSE b/LICENSE index 57e96434..3800ec13 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016 - Present Changkun Ou +Copyright (c) 2016 - 2020 Changkun Ou Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f957a0de --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +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 \ No newline at end of file diff --git a/README-zh-cn.md b/README-zh-cn.md new file mode 100644 index 00000000..4b06afa7 --- /dev/null +++ b/README-zh-cn.md @@ -0,0 +1,64 @@ +logo + +# 现代 C++ 教程:高速上手 C++11/14/17/20 + +![](https://img.shields.io/travis/changkun/modern-cpp-tutorial/master?style=flat-square) [![](https://img.shields.io/badge/language-English-blue.svg?style=flat-square)](./README.md) [![](https://img.shields.io/badge/language-简体中文-red.svg?style=flat-square)](./README-zh-cn.md) [![](https://img.shields.io/badge/€-donate-ff69b4.svg?style=flat-square)](./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)。 + +

本项目还由以下产品提供赞助支持:

+

+ + + +

+ +## 许可 + +知识共享许可协议 + +本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](./LICENSE)。 diff --git a/README.md b/README.md index e50cddbc..45df2bec 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,68 @@ -# 《高速上手 C++11/14/17》 +logo -> 本书正在向全面介绍 C++17 特性的内容过度,尽请期待。 +# Modern C++ Tutorial: C++11/14/17/20 On the Fly -![](assets/cover-2nd.png) +![](https://img.shields.io/travis/changkun/modern-cpp-tutorial/master?style=flat-square) [![](https://img.shields.io/badge/language-English-blue.svg?style=flat-square)](./README.md) [![](https://img.shields.io/badge/language-简体中文-red.svg?style=flat-square)](./README-zh-cn.md) [![](https://img.shields.io/badge/€-donate-ff69b4.svg?style=flat-square)](./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. -本书号称『高速上手』,从内容上对二十一世纪二十年代之前产生 C++ 的相关特性做了非常相对全面的介绍,读者可以自行根据下面的目录选取感兴趣的内容进行学习,快速熟悉需要了解的内容。这些特性并不需要全部掌握,只需针对自己的使用需求和特定的应用场景,学习、查阅最适合自己的新特性即可。 +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. -此外,笔者希望读者在阅读本书后,能够努力在新项目中直接使用 C++17,并努力将旧项目逐步迁移到 C++17。也算是笔者为推进现代 C++ 的普及贡献了一些绵薄之力。 +## 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. -1. 本书假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++进行编码的人、渴望在短时间内迅速了解**现代 C++** 特性的人非常适合阅读本书; -2. 本书一定程度上介绍了一些现代 C++ 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习现代 C++ 的读者,本书的定位系**现代 C++ 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 **现代 C++** 的熟悉度。 +- This book introduces, to a certain extent, the dark magic of modern C++. However, these magic tricks are very limited, they are not suitable for readers who want to learn advanced C++. The purpose of this book is offering a quick start for modern C++. Of course, advanced readers can also use this book to review and examine themselves on modern C++. -## 开始阅读 +## Start -你可以从[这里](./book/toc.md)开始阅读本书。 +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)中找到书中介绍过的全部的源码,所有代码按章节组织,文件夹名称为章节序号。 +## 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)找到习题的答案,文件夹名称为章节序号。 +## 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. -1. 笔者时间和水平有限,如果读者发现书中内容的错误,欢迎提 [issue](https://github.com/changkun/cpp1x-tutorial/issues); -2. 本书有以下读者交流群,有兴趣的读者可以加入,加群需正确回答加群密码 `github`: +## Website -![](assets/qq-group.png) +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: -[Recolic Keghart](https://www.gitbook.com/@recolic), [sinomiko](https://www.gitbook.com/@sinomiko), [Wang Zhenhua](https://www.gitbook.com/@jackwish), [asm warrior](https://www.gitbook.com/@asmwarrior), [garicc](https://www.gitbook.com/@ihpy), [Wenhan Jiang](https://www.gitbook.com/@jiangwenhan), [liang xiao](https://www.gitbook.com/@liangx8), [slivermeteor](https://github.com/slivermeteor) +```bash +$ make build +``` -## 赞助 +## Acknowledgements -如果你认为本书对你起到了帮助,并希望赞助作者,可以通过下面的二维码给予支持: +This book was originally written in Chinese by [Changkun Ou](https://changkun.de). -|微信|支付宝| -|:--:|:--:| -|![](assets/wechat.jpg) | ![](assets/alipay.jpg)| +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). -知识共享许可协议 +

This project is also supported by:

+

+ + + +

-本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](./LICENSE)。 +## Licenses + +Creative Commons License
This work was written by [Ou Changkun](https://changkun.de) and licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. The code of this repository is open sourced under the [MIT license](./LICENSE). diff --git a/assets/cover-2nd-en-logo.png b/assets/cover-2nd-en-logo.png new file mode 100644 index 00000000..050a18e0 Binary files /dev/null and b/assets/cover-2nd-en-logo.png differ diff --git a/assets/cover-2nd-en.afphoto b/assets/cover-2nd-en.afphoto new file mode 100644 index 00000000..60d66375 Binary files /dev/null and b/assets/cover-2nd-en.afphoto differ diff --git a/assets/cover-2nd-en.png b/assets/cover-2nd-en.png new file mode 100644 index 00000000..3811060d Binary files /dev/null and b/assets/cover-2nd-en.png differ diff --git a/assets/cover-2nd-logo.png b/assets/cover-2nd-logo.png new file mode 100644 index 00000000..a05a42a1 Binary files /dev/null and b/assets/cover-2nd-logo.png differ diff --git a/assets/cover-2nd.afphoto b/assets/cover-2nd.afphoto new file mode 100644 index 00000000..a313ae3a Binary files /dev/null and b/assets/cover-2nd.afphoto differ diff --git a/assets/cover-2nd.png b/assets/cover-2nd.png index 92ea730f..c89122bb 100644 Binary files a/assets/cover-2nd.png and b/assets/cover-2nd.png differ diff --git a/assets/donate.md b/assets/donate.md new file mode 100644 index 00000000..88ca78d1 --- /dev/null +++ b/assets/donate.md @@ -0,0 +1,20 @@ +--- +title: 资助 +type: about +order: 1 +--- + +## Donate + +I would love if you support me to make the book better: + +[![](https://img.shields.io/badge/donate-PayPal-104098.svg?style=popout-square&logo=PayPal)](https://www.paypal.me/changkunde/4.99eur) + +## 资助 + +如果你认为本书对你起到了帮助,并希望赞助作者,可以通过下面的二维码给予支持: + +|微信|支付宝| +|:--:|:--:| +|![](../assets/wechat.jpg) | ![](../assets/alipay.jpg)| + diff --git a/assets/comparison.png b/assets/figures/comparison.png similarity index 100% rename from assets/comparison.png rename to assets/figures/comparison.png diff --git a/assets/pointers1.png b/assets/figures/pointers1.png similarity index 100% rename from assets/pointers1.png rename to assets/figures/pointers1.png diff --git a/assets/figures/pointers1_en.png b/assets/figures/pointers1_en.png new file mode 100644 index 00000000..6a96338f Binary files /dev/null and b/assets/figures/pointers1_en.png differ diff --git a/assets/pointers2.png b/assets/figures/pointers2.png similarity index 100% rename from assets/pointers2.png rename to assets/figures/pointers2.png diff --git a/assets/qq-group.png b/assets/qq-group.png deleted file mode 100644 index c567cd76..00000000 Binary files a/assets/qq-group.png and /dev/null differ diff --git a/book/0-preface.md b/book/0-preface.md deleted file mode 100644 index 0a0a3d1a..00000000 --- a/book/0-preface.md +++ /dev/null @@ -1,39 +0,0 @@ -# 高速上手 C++ 11/14/17 - -## 引言 - -C++ 是一个用户群体相当大的语言。从 C++98 的出现到 C++11 的正式定稿经历了长达十年多之久的积累。C++14/17 则是作为对 C++11 的重要补充和优化,所有这些新标准中扩充的特性,给 C++ 这门语言注入了新的活力。 -那些还在坚持使用**传统 C++**(本书把 C++98 及其之前的 C++ 特性均称之为传统 C++)而未接触过 C++11/14/17 的 C++ 程序员在见到诸如 Lambda 表达式这类全新特性时,甚至会流露出『学的不是同一门语言』的惊叹之情。 - -**C++1x** (或**现代 C++**,本书中均指 C++11/14/17) 为传统 C++ 注入的大量特性使得整个 C++ 变得更加像一门现代化的语言。C++1x 不仅仅增强了 C++ 语言自身的可用性,`auto` 关键字语义的修改使得我们更加有信心来操控极度复杂的模板类型。同时还对语言运行期进行了大量的强化,Lambda 表达式的出现让 C++ 具有了『匿名函数』的『闭包』特性,而这一特性几乎在现代的编程语言(诸如 Python/Swift/... )中已经司空见惯,右值引用的出现解决了 C++ 长期以来被人诟病的临时对象效率问题等等。 - -C++17 则是近三年依赖 C++ 社区一致推进的方向,也指出了**现代C++**编程的一个重要发展方向。尽管它的出现并不如 C++11 的分量之重,但它包含了大量小而美的语言与特性(例如结构化绑定),这些特性的出现再一次修正了我们在 C++ 中的编程范式。 - -现代 C++ 还为自身的标准库增加了非常多的工具和方法,诸如在语言自身标准的层面上制定了 `std::thread`,从而支持了并发编程,在不同平台上不再依赖于系统底层的 API,实现了语言层面的跨平台支持;`std::regex` 提供了完整的正则表达式支持等等。C++98 已经被实践证明了是一种非常成功的『范型』,而 C++1x 的出现,则进一步推动这种范型,让 C++ 成为系统程序设计和库开发更好的语言。 - -总而言之,我们作为 C++ 的拥护与实践者,始终保持接纳新事物的开放心态,才能更快的推进 C++ 的发展,使得这门古老而又新颖的语言更加充满活力。 - -## 目标读者 - -1. 本书假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++进行编码的人、渴望在短时间内迅速了解**现代 C++** 特性的人非常适合阅读本书; -2. 本书一定程度上介绍了一些现代 C++ 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习现代 C++ 的读者,本书的定位系**现代 C++ 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 **现代 C++** 的熟悉度。 - -## 本书目的 - -本书号称『高速上手』,从内容上对二十一世纪二十年代之前产生 C++ 的相关特性做了非常相对全面的介绍,读者可以自行根据下面的目录选取感兴趣的内容进行学习,快速熟悉需要了解的内容。这些特性并不需要全部掌握,只需针对自己的使用需求和特定的应用场景,学习、查阅最适合自己的新特性即可。 - -同时,本书在介绍这些特性的过程中,尽可能简单明了的介绍了这些特性产生的历史背景和技术需求,这为理解这些特性、运用这些特性提供了很大的帮助。 - -此外,笔者希望读者在阅读本书后,能够努力在新项目中直接使用 C++17,并努力将旧项目逐步迁移到 C++17。也算是笔者为推进现代 C++ 的普及贡献了一些绵薄之力。 - -## 相关代码 - -本书每章中都出现了大量的代码,如果你在跟随本书介绍特性的思路编写自己的代码遇到问题时,不妨读一读随书附上的源码,你可以在[这里](../code)中找到书中介绍过的全部的源码,所有代码按章节组织,文件夹名称为章节序号。 - -[返回目录](./toc.md) | 上一章 | [下一章:迈向 C++11/14/17](./01-intro.md) - -## 许可 - -知识共享许可协议 - -本书系[欧长坤](https://github.com/changkun)著,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../LICENSE)。 \ No newline at end of file diff --git a/book/03-runtime.md b/book/03-runtime.md deleted file mode 100644 index 5940f86c..00000000 --- a/book/03-runtime.md +++ /dev/null @@ -1,442 +0,0 @@ -# 第三章 语言运行期的强化 - -> 内容修订中 - -## 一、本节内容 - -本节内容包括: - -* 语言运行期的强化 -* lambda 表达式 -* lambda 表达式基础 -* 值捕获 -* 引用捕获 -* 隐式捕获 -* 表达式捕获 -* 泛型 lambda -* 函数对象包装器 -* std::function -* std::bind/std::placeholder -* 右值引用 -* 左值、右值的纯右值、将亡值、右值 -* 右值引用和左值引用 -* 移动语义 -* 完美转发 - -## 二、Lambda 表达式 - -Lambda 表达式是 C++11 中最重要的新特性之一,而 Lambda 表达式,实际上就是提供了一个类似匿名函数的特性,而匿名函数则是在需要一个函数,但是又不想费力去命名一个函数的情况下去使用的。这样的场景其实有很多很多,所以匿名函数几乎是现代编程语言的标配。 - -### Lambda 表达式基础 - -Lambda 表达式的基本语法如下: - -``` -[捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 { -// 函数体 -} -``` - -上面的语法规则除了 `[捕获列表]` 内的东西外,其他部分都很好理解,只是一般函数的函数名被略去,返回值使用了一个 `->` 的形式进行(我们在上一节前面的尾返回类型已经提到过这种写法了)。 - -所谓捕获列表,其实可以理解为参数的一种类型,lambda 表达式内部函数体在默认情况下是不能够使用函数体外部的变量的,这时候捕获列表可以起到传递外部数据的作用。根据传递的行为,捕获列表也分为以下几种: - -**1. 值捕获** - -与参数传值类似,值捕获的前期是变量可以拷贝,不同之处则在于,被捕获的变量在 lambda 表达式被创建时拷贝,而非调用时才拷贝: - -```cpp -void learn_lambda_func_1() { - int value_1 = 1; - auto copy_value_1 = [value_1] { - return value_1; -}; -value_1 = 100; -auto stored_value_1 = copy_value_1(); - // 这时, stored_value_1 == 1, 而 value_1 == 100. - // 因为 copy_value_1 在创建时就保存了一份 value_1 的拷贝 -} -``` - -**2. 引用捕获** - -与引用传参类似,引用捕获保存的是引用,值会发生变化。 - -```cpp -void learn_lambda_func_2() { - int value_2 = 1; - auto copy_value_2 = [&value_2] { - return value_2; -}; -value_2 = 100; -auto stored_value_2 = copy_value_2(); - // 这时, stored_value_2 == 100, value_1 == 100. - // 因为 copy_value_2 保存的是引用 -} -``` - -**3. 隐式捕获** - -手动书写捕获列表有时候是非常复杂的,这种机械性的工作可以交给编译器来处理,这时候可以在捕获列表中写一个 `&` 或 `=` 向编译器声明采用 引用捕获或者值捕获. - -总结一下,捕获提供了lambda 表达式对外部值进行使用的功能,捕获列表的最常用的四种形式可以是: - -* \[\] 空捕获列表 -* \[name1, name2, ...\] 捕获一系列变量 -* \[&\] 引用捕获, 让编译器自行推导捕获列表 -* \[=\] 值捕获, 让编译器执行推导应用列表 - -**4. 表达式捕获\(C++14\)** - -> 这部分内容需要了解后面马上要提到的右值引用以及智能指针 - -上面提到的值捕获、引用捕获都是已经在外层作用域声明的变量,因此这些捕获方式捕获的均为左值,而不能捕获右值。 - -C++14 给与了我们方便,允许捕获的成员用任意的表达式进行初始化,这就允许了右值的捕获,被声明的捕获变量类型会根据表达式进行判断,判断方式与使用 `auto` 本质上是相同的: - -```cpp -#include -#include - -int main() { - auto important = std::make_unique(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; - return 0; -} -``` - -在上面的代码中,`important` 是一个独占指针,是不能够被捕获到的,这时候我们需要将其转移为右值,在表达式中初始化。 - -### 泛型 Lambda - -上一节中我们提到了 `auto` 关键字不能够用在参数表里,这是因为这样的写法会与模板的功能产生冲突。但是 Lambda 表达式并不是普通函数,所以 Lambda 表达式并不能够模板化。这就为我们造成了一定程度上的麻烦:参数表不能够泛化,必须明确参数表类型。 - -幸运的是,这种麻烦只存在于 C++11 中,从 C++14 开始,Lambda 函数的形式参数可以使用 `auto` 关键字来产生意义上的泛型: - -```cpp -auto add = [](auto x, auto y) { - return x+y; -}; - -add(1, 2); -add(1.1, 2.2); -``` - -## 二、函数对象包装器 - -这部分内容虽然属于标准库的一部分,但是从本质上来看,它却增强了 C++ 语言运行时的能力,这部分内容也相当重要,所以放到这里来进行介绍。 - -### std::function - -Lambda 表达式的本质是一个函数对象,当 Lambda 表达式的捕获列表为空时,Lambda 表达式还能够作为一个函数指针进行传递,例如: - -```cpp -#include - -using foo = void(int); // 定义函数指针, using 的使用见上一节中的别名语法 - void functional(foo f) { - f(1); -} - -int main() { - auto f = [](int value) { - std::cout << value << std::endl; - }; - functional(f); // 函数指针调用 - f(1); // lambda 表达式调用 - return 0; -} -``` - -上面的代码给出了两种不同的调用形式,一种是将 Lambda 作为函数指针传递进行调用,而另一种则是直接调用 Lambda 表达式,在 C++11 中,统一了这些概念,将能够被调用的对象的类型,统一称之为可调用类型。而这种类型,便是通过 `std::function` 引入的。 - -C++11 `std::function` 是一种通用、多态的函数封装,它的实例可以对任何可以调用的目标实体进行存储、复制和调用操作,它也是对 C++中现有的可调用实体的一种类型安全的包裹(相对来说,函数指针的调用不是类型安全的),换句话说,就是函数的容器。当我们有了函数的容器之后便能够更加方便的将函数、函数指针作为对象进行处理。例如: - -```cpp -#include -#include - -int foo(int para) { - return para; -} - -int main() { - // std::function 包装了一个返回值为 int, 参数为 int 的函数 - std::function func = foo; - - int important = 10; - std::function func2 = [&](int value) -> int { - return 1+value+important; - }; - std::cout << func(10) << std::endl; - std::cout << func2(10) << std::endl; -} -``` - -### std::bind/std::placeholder - -而 `std::bind` 则是用来绑定函数调用的参数的,它解决的需求是我们有时候可能并不一定能够一次性获得调用某个函数的全部参数,通过这个函数,我们可以将部分调用参数提前绑定到函数身上成为一个新的对象,然后在参数齐全后,完成调用。例如: - -```cpp -int foo(int a, int b, int c) { - ; -} -int main() { - // 将参数1,2绑定到函数 foo 上,但是使用 std::placeholders::_1 来对第一个参数进行占位 - auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2); - // 这时调用 bindFoo 时,只需要提供第一个参数即可 - bindFoo(1); -} -``` - -> **提示:**注意 `auto` 关键字的妙用。有时候我们可能不太熟悉一个函数的返回值类型,但是我们却可以通过 `auto` 的使用来规避这一问题的出现。 - -## 三、右值引用 - -右值引用是 C++11 引入的与 Lambda 表达式齐名的重要特性之一。它的引入解决了 C++ 中大量的历史遗留问题,消除了诸如 `std::vector`、`std::string` 之类的额外开销,也才使得函数对象容器 `std::function` 成为了可能。 - -### 左值、右值的纯右值、将亡值、右值 - -要弄明白右值引用到底是怎么一回事,必须要对左值和右值做一个明确的理解。 - -**左值\(lvalue, left value\)**,顾名思义就是赋值符号左边的值。准确来说,左值是表达式(不一定是赋值表达式)后依然存在的持久对象。 - -**右值\(rvalue, right value\)**,右边的值,是指表达式结束后就不再存在的临时对象。 - -而 C++11 中为了引入强大的右值引用,将右值的概念进行了进一步的划分,分为:纯右值、将亡值。 - -**纯右值\(prvalue, pure rvalue\)**,纯粹的右值,要么是纯粹的字面量,例如 `10`, `true`;要么是求值结果相当于字面量或匿名临时对象,例如 `1+2`。非引用返回的临时变量、运算表达式产生的临时变量、原始字面量、Lambda 表达式都属于纯右值。 - -**将亡值\(xvalue, expiring value\)**,是 C++11 为了引入右值引用而提出的概念(因此在传统 C++中,纯右值和右值是统一个概念),也就是即将被销毁、却能够被移动的值。 - -将亡值可能稍有些难以理解,我们来看这样的代码: - -```cpp -std::vector foo() { -std::vector temp = {1, 2, 3, 4}; - return temp; -} - -std::vector v = foo(); -``` - -在这样的代码中,函数 `foo` 的返回值 `temp` 在内部创建然后被赋值给 `v`,然而 `v` 获得这个对象时,会将整个 temp 拷贝一份,然后把 `temp` 销毁,如果这个 `temp` 非常大,这将造成大量额外的开销(这也就是传统 C++ 一直被诟病的问题)。在最后一行中,`v` 是左值、`foo()` 返回的值就是右值(也是纯右值)。 - -但是,`v` 可以被别的变量捕获到,而 `foo()` 产生的那个返回值作为一个临时值,一旦被 `v` 复制后,将立即被销毁,无法获取、也不能修改。 - -将亡值就定义了这样一种行为:临时的值能够被识别、同时又能够被移动。 - -### 右值引用和左值引用 - -需要拿到一个将亡值,就需要用到右值引用的申明:`T &&`,其中 `T` 是类型。右值引用的声明让这个临时值的生命周期得以延长、只要变量还活着,那么将亡值将继续存活。 - -C++11 提供了 `std::move` 这个方法将左值参数无条件的转换为右值,有了它我们就能够方便的获得一个右值临时对象,例如: - -```cpp -#include -#include - -void reference(std::string& str) { - std::cout << "左值" << std::endl; -} -void reference(std::string&& str) { - std::cout << "右值" << std::endl; -} - -int main() -{ - std::string lv1 = "string,"; // lv1 是一个左值 - // std::string&& r1 = s1; // 非法, 右值引用不能引用左值 - std::string&& rv1 = std::move(lv1); // 合法, std::move可以将左值转移为右值 - std::cout << rv1 << std::endl; // string, - - const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的申明周期 - // lv2 += "Test"; // 非法, 引用的右值无法被修改 - std::cout << lv2 << std::endl; // string,string - - std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象声明周期 - rv2 += "Test"; // 合法, 非常量引用能够修改临时变量 - std::cout << rv2 << std::endl; // string,string,string, - - reference(rv2); // 输出左值 - - return 0; -} -``` - -**注意**:`rv2` 虽然引用了一个右值,但由于它是一个引用,所以 `rv2` 依然是一个左值。 - -### 移动语义 - -传统 C++ 通过拷贝构造函数和赋值操作符为类对象设计了拷贝/复制的概念,但为了实现对资源的移动操作,调用者必须使用先复制、再析构的方式,否则就需要自己实现移动对象的接口。试想,搬家的时候是把家里的东西直接搬到新家去,而不是将所有东西复制一份(重买)再放到新家、再把原来的东西全部扔掉(销毁),这是非常反人类的一件事情。 - -传统的 C++ 没有区分『移动』和『拷贝』的概念,造成了大量的数据移动,浪费时间和空间。右值引用的出现恰好就解决了这两个概念的混淆问题,例如: - -```cpp -#include -class A { -public: - int *pointer; - A():pointer(new int(1)) { std::cout << "构造" << pointer << std::endl; } - A(A& a):pointer(new int(*a.pointer)) { std::cout << "拷贝" << pointer << std::endl; } // 无意义的对象拷贝 - A(A&& a):pointer(a.pointer) { a.pointer = nullptr;std::cout << "移动" << pointer << std::endl; } - ~A(){ std::cout << "析构" << pointer << std::endl; delete pointer; } -}; -// 防止编译器优化 -A return_rvalue(bool test) { - A a,b; - if(test) return a; - else return 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; -} -``` - -在上面的代码中: - -1. 首先会在 `return_rvalue` 内部构造两个 `A` 对象,于是获得两个构造函数的输出; -2. 函数返回后,产生一个将亡值,被 `A` 的移动构造(`A(A&&)`)引用,从而延长生命周期,并将这个右值中的指针拿到,保存到了 `obj` 中,而将亡值的指针被设置为 `nullptr`,防止了这块内存区域被销毁。 - -从而避免了无意义的拷贝构造,加强了性能。再来看看涉及标准库的例子: - -```cpp -#include // std::cout -#include // std::move -#include // std::vector -#include // std::string - -int main() { - -std::string str = "Hello world."; -std::vector v; - - // 将使用 push_back(const T&), 即产生拷贝行为 - v.push_back(str); - // 将输出 "str: Hello world." - std::cout << "str: " << str << std::endl; - - // 将使用 push_back(const T&&), 不会出现拷贝行为 - // 而整个字符串会被移动到 vector 中,所以有时候 std::move 会用来减少拷贝出现的开销 - // 这步操作后, str 中的值会变为空 - v.push_back(std::move(str)); - // 将输出 "str: " - std::cout << "str: " << str << std::endl; - - return 0; -} -``` - -### 完美转发 - -前面我们提到了,一个声明的右值引用其实是一个左值。这就为我们进行参数转发(传递)造成了问题: - -```cpp -void reference(int& v) { - std::cout << "左值" << std::endl; -} -void reference(int&& v) { - std::cout << "右值" << std::endl; -} -template -void pass(T&& v) { - std::cout << "普通传参:"; - reference(v); // 始终调用 reference(int& ) -} -int main() { - std::cout << "传递右值:" << std::endl; - pass(1); // 1是右值, 但输出左值 - - std::cout << "传递左值:" << std::endl; - int v = 1; - pass(v); // r 是左引用, 输出左值 - - return 0; -} -``` - -对于 `pass(1)` 来说,虽然传递的是右值,但由于 `v` 是一个引用,所以同时也是左值。因此 `reference(v)` 会调用 `reference(int&)`,输出『左值』。而对于`pass(v)`而言,`v`是一个左值,为什么会成功传递给 `pass(T&&)` 呢? - -这是基于**引用坍缩规则**的:在传统 C++ 中,我们不能够对一个引用类型继续进行引用,但 C++ 由于右值引用的出现而放宽了这一做法,从而产生了引用坍缩规则,允许我们对引用进行引用,既能左引用,又能右引用。但是却遵循如下规则: - -| 函数形参类型 | 实参参数类型 | 推导后函数形参类型 | -| :---: | :---: | :---: | -| T& | 左引用 | T& | -| T& | 右引用 | T& | -| T&& | 左引用 | T& | -| T&& | 右引用 | T&& | - -因此,模板函数中使用 `T&&` 不一定能进行右值引用,当传入左值时,此函数的引用将被推导为左值。更准确的讲,**无论模板参数是什么类型的引用,当且仅当实参类型为右引用时,模板参数才能被推导为右引用类型**。这才使得 `v` 作为左值的成功传递。 - -完美转发就是基于上述规律产生的。所谓完美转发,就是为了让我们在传递参数的时候,保持原来的参数类型(左引用保持左引用,右引用保持右引用)。为了解决这个问题,我们应该使用 `std::forward` 来进行参数的转发(传递): - -```cpp -#include -#include -void reference(int& v) { - std::cout << "左值引用" << std::endl; -} -void reference(int&& v) { - std::cout << "右值引用" << std::endl; -} -template -void pass(T&& v) { - std::cout << "普通传参:"; - reference(v); - std::cout << "std::move 传参:"; - reference(std::move(v)); - std::cout << "std::forward 传参:"; - reference(std::forward(v)); - -} -int main() { - std::cout << "传递右值:" << std::endl; - pass(1); - - std::cout << "传递左值:" << std::endl; - int v = 1; - pass(v); - - return 0; -} -``` - -输出结果为: - -``` -传递右值: -普通传参:左值引用 -std::move 传参:右值引用 -std::forward 传参:右值引用 -传递左值: -普通传参:左值引用 -std::move 传参:右值引用 -std::forward 传参:左值引用 -``` - -无论传递参数为左值还是右值,普通传参都会将参数作为左值进行转发,所以 `std::move` 总会接受到一个左值,从而转发调用了`reference(int&&)` 输出右值引用。 - -唯独 `std::forward` 即没有造成任何多余的拷贝,同时**完美转发**\(传递\)了函数的实参给了内部调用的其他函数。 - -> `std::forward` 和 `std::move` 一样,没有做任何事情,`std::move` 单纯的将左值转化为右值,`std::forward` 也只是单纯的将参数做了一个类型的转换,从是线上来看,`std::forward(v)` 和 `static_cast(v)` 是完全一样的。 - -## 总结 - -本节介绍了 C++11/14 中对语言可用性的增强,其中笔者认为本节中提到的所有特性都是值得掌握的: - -1. Lambda 表达式 -2. 函数对象容器 std::function -3. 右值引用 - -## 许可 - -知识共享许可协议 - -本教程由[欧长坤](https://github.com/changkun)撰写,采用[知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议](http://creativecommons.org/licenses/by-nc-nd/4.0/)许可。项目中代码使用 MIT 协议开源,参见[许可](../LICENSE)。 \ No newline at end of file diff --git a/book/04-containers.md b/book/04-containers.md deleted file mode 100644 index d6a52f5a..00000000 --- a/book/04-containers.md +++ /dev/null @@ -1,254 +0,0 @@ -# 第四章 对标准库的扩充:新增容器 - -> 内容修订中 - -## 一、本节内容 - -本节内容包括: - -* 对标准库的扩充: 新增容器 -* `std::byte` -* `std::any` `std::optional` `std::variant` -* `std::string_view` -* `std::array` -* `std::forward_list` -* `std::unordered_set` -* `std::unordered_map` -* `std::tuple` -* 基本操作 -* 运行期索引 -* 合并与迭代 - -## 二、std::array 和 std::forward\_list - -### std::array - -看到这个容器的时候肯定会出现这样的问题: - -1. 为什么要引入 `std::array` 而不是直接使用 `std::vector`? -2. 已经有了传统数组,为什么要用 `std::array`? - -先回答第一个问题,`std::vector` 太强大了,以至于我们没有必要为了去敲碎一个鸡蛋而用一个钉锤。使用 `std::array` 保存在栈内存中,相比堆内存中的 `std::vector`,我们就能够灵活的访问这里面的元素,从而获得更高的性能;同时正式由于其堆内存存储的特性,有些时候我们还需要自己负责释放这些资源。 - -而第二个问题就更加简单,使用`std::array`能够让代码变得更加现代,且封装了一些操作函数,同时还能够友好的使用标准库中的容器算法等等,比如 `std::sort`。 - -`std::array` 会在编译时创建一个固定大小的数组,`std::array` 不能够被隐式的转换成指针,使用 `std::array` 很简单,只需指定其类型和大小即可: - -```cpp -std::array arr= {1,2,3,4}; - -int len = 4; -std::array arr = {1,2,3,4}; // 非法, 数组大小参数必须是常量表达式 -``` - -当我们开始用上了 `std::array` 时,难免会遇到要将其兼容 C 风格的接口,这里有三种做法: - -```cpp -void foo(int *p, int len) { - return; -} - -std::array arr = {1,2,3,4}; - -// C 风格接口传参 -// foo(arr, arr.size()); // 非法, 无法隐式转换 -foo(&arr[0], arr.size()); -foo(arr.data(), arr.size()); - -// 使用 `std::sort` -std::sort(arr.begin(), arr.end()); -``` - -### std::forward\_list - -`std::forward_list` 是一个列表容器,使用方法和 `std::list` 基本类似,因此我们就不花费篇幅进行介绍了。 - -需要知道的是,和 `std::list` 的双向链表的实现不同,`std::forward_list` 使用单向链表进行实现,提供了 `O(1)` 复杂度的元素插入,不支持快速随机访问(这也是链表的特点),也是标准库容器中唯一一个不提供 `size()` 方法的容器。当不需要双向迭代时,具有比 `std::list` 更高的空间利用率。 - -## 三、无序容器 - -我们已经熟知了传统 C++ 中的有序容器 `std::map`/`std::set`,这些元素内部通过红黑树进行实现,插入和搜索的平均复杂度均为 `O(log(size))`。在插入元素时候,会根据 `<` 操作符比较元素大小并判断元素是否相同,并选择合适的位置插入到容器中。当对这个容器中的元素进行遍历时,输出结果会按照 `<` 操作符的顺序来逐个遍历。 - -而无序容器中的元素是不进行排序的,内部通过 Hash 表实现,插入和搜索元素的平均复杂度为 `O(constant)`,在不关心容器内部元素顺序时,能够获得显著的性能提升。 - -C++11 引入了两组无序容器:`std::unordered_map`/`std::unordered_multimap` 和 `std::unordered_set`/`std::unordered_multiset`。 - -它们的用法和原有的 `std::map`/`std::multimap`/`std::set`/`set::multiset` 基本类似,由于这些容器我们已经很熟悉了,便不一一举例,我们直接来比较一下`std::map`和`std::unordered_map`: - -```cpp -#include -#include -#include -#include