**C++17** 是C++标准委员会于2017年制定的标准（ISO/IEC 14882），接替C++14标准，后被C++20标准取代。

历史
--

在C++标准委员会制定此次的三年期标准之前，C++17标准的发布日期是不确定的。在这段日期中，C++17草案（draft）也被称为**C++1z**，正如C++11草案名C++0x或C++1x，C++14草案名C++1y。C++17标准于2017年3月完成国际标准草案（Draft International Standard DIS）阶段，并于同年12月完成最终标准发布。17标准对C++语法进行了加强，并对标准模板库进行了些许修改，如给<algorithm>库中算法增加了执行策略execution以支持并行计算。

新功能
---

* 靜態斷言`static_assert`無需提供出錯信息
* 移除 trigraphs
* 具有模板形式的模板（template template）参数允许使用 typename（之前仅允许使用 class）
* std::uncaught\_exceptions 取代 std::uncaught\_exception
* 变长参数模板的Folding运算
* 容器存取操作表示方法的统一化（Uniform container access）
* 连续型迭代器（Contiguous Iterators）
* 新增特殊數學函數
* 引进Library Fundamentals TS I中的内容

### 类模板的模板参数推导

模板类构造函数能自动推导模板参数的类型(Class template argument deduction, CTAD)。例如允許以`pair(5.0, false)` 取代`pair<double,bool>(5.0, false)`

```
template <typename T = float>
struct MyContainer {
 T val;
 MyContainer() : val{} {}
 MyContainer(T val) : val{val} {}
 // ...
};
MyContainer c1 {1}; // OK MyContainer<int>
MyContainer c2; // OK MyContainer<float>

```

任何指定变量或变量模板的初始化的声明，其声明的类型是类模板（可能是 cv 限定的）。如std::pair p(2, 4.5); // deduces to std::pair<int, double> p(2, 4.5);
new表达式，如template<class T>struct A{ A(T, T);}; auto y = new A{1, 2}; // allocated type is A<int>

### 用auto声明非类型模板参数

依据auto的推到规则，在可允许类型的非类型模板参数情况下，可从实参类型推导出模板参数：

```
template <auto... seq>
struct my\_integer\_sequence {
 // Implementation here ...
};

// Explicitly pass type `int` as template argument.
auto seq = std::integer\_sequence<int, 0, 1, 2>();
// Type is deduced to be `int`.
auto seq2 = my\_integer\_sequence<0, 1, 2>();

```

### 折叠表达式

折叠表达式在运算符上执行模板参数包的展开计算。

* 单元折叠：形如 (... op e) or (e op ...)表达式，其中 op 是折叠运算符，e 是未展开的模板参数包。
* 二元折叠：形如(e1 op ... op e2)表达式， 其中 op 是折叠运算符，e1或e2 是未展开的模板参数包。

```
template <typename... Args>
bool logicalAnd(Args... args) {
 // Binary folding.
 return (true && ... && args);
}
bool b = true;
bool& b2 = b;
logicalAnd(b, b2, true); // == true

template <typename... Args>
auto sum(Args... args) {
 // Unary folding.
 return (... + args);
}
sum(1.0, 2.0f, 3); // == 6.0

```

### 大括号初始化列表的新的自动推导规则

以前`auto x {3};` 将被推导为`std::initializer_list<int>`, 现在推导为`int`。

```
auto x1 {1, 2, 3}; // error: not a single element
auto x2 = {1, 2, 3}; // x2 is std::initializer\_list<int>
auto x3 {3}; // x3 is int
auto x4 {3.0}; // x4 is double

```

### constexpr lambda

```
auto identity = [](int n) constexpr { return n; };
static\_assert(identity(123) == 123);

constexpr auto add = [](int x, int y) {
 auto L = [=] { return x; };
 auto R = [=] { return y; };
 return [=] { return L() + R(); };
};

static\_assert(add(1, 2)() == 3);

constexpr int addOne(int n) {
 return [n] { return n + 1; }();
}

static\_assert(addOne(1) == 2);

```

### Lambda函数中按值捕获this指针

以前this指针在lambda函数只能按引用捕获。现在 \*this 捕获对象的副本，而 this (C++11)继续捕获引用。

```
struct MyObj {
 int value {123};
 auto getValueCopy() {
 return [\*this] { return value; };
 }
 auto getValueRef() {
 return [this] { return value; };
 }
};
MyObj mo;
auto valueCopy = mo.getValueCopy();
auto valueRef = mo.getValueRef();
mo.value = 321;
valueCopy(); // 123
valueRef(); // 321

```

### 内联变量

过去关键字inline可用于函数声明，现在也可以用于变量声明，表示函数或定义可定义（但内容必须完全相同）多次。这允许在头文件中定义一个内联变量。

* 内联的函数或变量必须在编译单元内可达（但不一定在访问点之前）
* 在每个编译单元中都是内联声明的
* 内联变量必须是静态存储器（类静态成员或命名空间作用域变量），不能用于块作用域变量或函数作用域变量。

```
struct S {
 S() : id{count++} {}
 ~S() { count--; }
 int id;
 static inline int count{0}; // declare and initialize count to 0 within the class
};

```

### 嵌套的命名空间

如下：

```
namespace A {
 namespace B {
 namespace C {
 int i;
 }
 }
}

//The code above can be written like this:

namespace A::B::C {
 int i;
}

```

### 结构化绑定

变量定义初始化时，允许形如 `auto [ x, y, z ] = expr;`，其中expr的"元组类似的对象"包括`std::tuple, std::pair, std::array`等聚合结构。

```
using Coordinate = std::pair<int, int>;
Coordinate origin() {
 return Coordinate{0, 0};
}

const auto [ x, y ] = origin();
x; // == 0
y; // == 0

std::unordered\_map<std::string, int> mapping {
 {"a", 1},
 {"b", 2},
 {"c", 3}
};

// Destructure by reference.
for (const auto& [key, value] : mapping) {
 // Do something with key and value
}

```

### 带初始化的选择语句

if和switch语句允许如下的变量声明和初始化：

```
{
 std::lock\_guard<std::mutex> lk(mx);
 if (v.empty()) v.push\_back(val);
}
// vs.
if (std::lock\_guard<std::mutex> lk(mx); v.empty()) {
 v.push\_back(val);
}

Foo gadget(args);
switch (auto s = gadget.status()) {
 case OK: gadget.zip(); break;
 case Bad: throw BadFoo(s.message());
}
// vs.
switch (Foo gadget(args); auto s = gadget.status()) {
 case OK: gadget.zip(); break;
 case Bad: throw BadFoo(s.message());
}

```

### constexpr if

```
//适用场景1：简化模版偏特化的写法
template <typename T>
constexpr bool isIntegral() {
 if constexpr (std::is\_integral<T>::value) {
 return true;
 } else {
 return false;
 }
}
static\_assert(isIntegral<int>() == true);
static\_assert(isIntegral<char>() == true);
static\_assert(isIntegral<double>() == false);
struct S {};
static\_assert(isIntegral<S>() == false);

//适用场景2：编写变参模版函数
template<int N, int...Ns>
int Sum()
{
 if constexpr(0 == sizeof...(Ns))
 return N;
 else
 return N+Sum<Ns...> ();
}

//使用场景3：替代enable\_if。编写模板函数时，经常要使用enable\_if语句来进行静态类型检查，保证模板输入的类型满足某种要求
template <typename T>
bool IsOdd(T input){
 if constexpr (std::is\_integral<T>::value) //
 return static\_cast<bool>(input % 2);
}

```

### utf8字面量

如下：

```
char x = u8'x';

```

### 枚举类型变量直接用大括号初始化

```
enum byte : unsigned char {};
byte b {0}; // OK
byte c {-1}; // ERROR
byte d = byte{1}; // OK
byte e = byte{256}; // ERROR

```

### fallthrough, nodiscard, maybe\_unused特性

C++17 引入了3个特性（attribute）: fallthrough, nodiscard, maybe\_unused.

```
//fallthrough指示编译器直通一个switch语句：
switch (n) {
 case 1: 
 // ...
 [[fallthrough]]
 case 2:
 // ...
 break;
}

//nodiscard引发一个警告，当具有该特性的函数或类的返回值被忽略：
[[nodiscard]] bool do\_something() {
 return is\_success; // true for success, false for failure
}

do\_something(); // warning: ignoring return value of 'bool do\_something()',
 // declared with attribute 'nodiscard'

// Only issues a warning when `error\_info` is returned by value.
struct [[nodiscard]] error\_info {
 // ...
};

error\_info do\_something() {
 error\_info ei;
 // ...
 return ei;
}

do\_something(); // warning: ignoring returned value of type 'error\_info',
 // declared with attribute 'nodiscard'

//maybe\_unused告诉编译器变量或参数可能未被使用：
void my\_callback(std::string msg, [[maybe\_unused]] bool error) {
 // Don't care if `msg` is an error message, just log it.
 log(msg);
}

```

### \_\_has\_include运算符

\_\_has\_include运算符用在#if 和 #elif表达式，以检查是否一个头文件或源文件可否包含。

```
//下例有两个相同作用的头文件，如果主文件不能包含， 就使用backup/experimental头文件：
#ifdef \_\_has\_include
# if \_\_has\_include(<optional>)
# include <optional>
# define have\_optional 1
# elif \_\_has\_include(<experimental/optional>)
# include <experimental/optional>
# define have\_optional 1
# define experimental\_optional
# else
# define have\_optional 0
# endif
#endif

//也可以用在不同位置、不同名字但具有相同地位的头文件的包含：
#ifdef \_\_has\_include
# if \_\_has\_include(<OpenGL/gl.h>)
# include <OpenGL/gl.h>
# include <OpenGL/glu.h>
# elif \_\_has\_include(<GL/gl.h>)
# include <GL/gl.h>
# include <GL/glu.h>
# else
# error No suitable OpenGL headers found.
# endif
#endif

```

### std::variant

std::variant模板类实现了类型安全的union

```
std::variant<int, double> v{ 12 };
std::get<int>(v); // == 12
std::get<0>(v); // == 12
v = 12.0;
std::get<double>(v); // == 12.0
std::get<1>(v); // == 12.0

```

### std::optional

std::optional类模板可能包含一个值或者可能不包含值：

```
std::optional<std::string> create(bool b) {
 if (b) {
 return "Godzilla";
 } else {
 return {};//或者 return std::nullopt;
 }
}

create(false).has\_value();//返回true或false
\*create(false);//返回值；如果无值，则返回T()
create(false).value\_or("empty"); // == "empty"
create(true).value(); // == "Godzilla"
// optional-returning factory functions are usable as conditions of while and if
if (auto str = create(true)) {
 // ...
}

```

### std::any

std::any模板类是类型安全的包含任何类型的值：

```
std::any x {5};
x.has\_value() // == true
std::any\_cast<int>(x) // == 5
std::any\_cast<int&>(x) = 10;
std::any\_cast<int>(x) // == 10

```

### std::string\_view

std::string\_view类是不拥有字符串的情况可以读取其值：

```
// Regular strings.
std::string\_view cppstr {"foo"};
// Wide strings.
std::wstring\_view wcstr\_v {L"baz"};
// Character arrays.
char array[3] = {'b', 'a', 'r'};
std::string\_view array\_v(array, std::size(array));

std::string str {" trim me"};
std::string\_view v {str};
v.remove\_prefix(std::min(v.find\_first\_not\_of(" "), v.size()));
str; // == "trim me"
v; // == "trim me"

```

std::string\_view不提供c\_str()成员函数。

### std::invoke

std::invoke用于调用"可调用对象"。

```
template <typename Callable>
class Proxy {
 Callable c;
public:
 Proxy(Callable c): c(c) {}
 template <class... Args>
 decltype(auto) operator()(Args&&... args) {
 // ...
 return std::invoke(c, std::forward<Args>(args)...);
 }
};
auto add = [](int x, int y) {
 return x + y;
};
Proxy<decltype(add)> p {add};
p(1, 2); // == 3

```

### std::apply

std::apply把std::tuple参数应用于可调用对象：

```
auto add = [](int x, int y) {
 return x + y;
};
std::apply(add, std::make\_tuple(1, 2)); // == 3

```

### std::filesystem

std::filesystem可操作文件、目录、路径：

```
const auto bigFilePath {"bigFileToCopy"};
if (std::filesystem::exists(bigFilePath)) {
 const auto bigFileSize {std::filesystem::file\_size(bigFilePath)};
 std::filesystem::path tmpPath {"/tmp"};
 if (std::filesystem::space(tmpPath).available > bigFileSize) {
 std::filesystem::create\_directory(tmpPath.append("example"));
 std::filesystem::copy\_file(bigFilePath, tmpPath.append("newFile"));
 }
}

```

### std::byte

std::byte类型既不是字符类型，也不是算术类型，实际上是枚举类型：

```
std::byte a {0};
std::byte b {0xFF};
int i = std::to\_integer<int>(b); // 0xFF
std::byte c = a & b;
int j = std::to\_integer<int>(c); // 0

```

### maps 和 sets 上更有效率地移动节点

零开销的节点操作：

```
//Moving elements from one map to another:
std::map<int, string> src {{1, "one"}, {2, "two"}, {3, "buckle my shoe"}};
std::map<int, string> dst {{3, "three"}};
dst.insert(src.extract(src.find(1))); // Cheap remove and insert of { 1, "one" } from `src` to `dst`.
dst.insert(src.extract(2)); // Cheap remove and insert of { 2, "two" } from `src` to `dst`.
// dst == { { 1, "one" }, { 2, "two" }, { 3, "three" } };

//Inserting an entire set:
std::set<int> src {1, 3, 5};
std::set<int> dst {2, 4, 5};
dst.merge(src);
// src == { 5 }
// dst == { 1, 2, 3, 4, 5 }

//Inserting elements which outlive the container:
auto elementFactory() {
 std::set<...> s;
 s.emplace(...);
 return s.extract(s.begin());
}
s2.insert(elementFactory());

//Changing the key of a map element:
std::map<int, string> m {{1, "one"}, {2, "two"}, {3, "three"}};
auto e = m.extract(2);
e.key() = 4;
m.insert(std::move(e));
// m == { { 1, "one" }, { 3, "three" }, { 4, "two" } }

```

### 并行算法

许多STL算法，如copy, find 和 sort，支持并行执行政策：seq, par, par\_unseq, unseq分别表示"sequentially", "parallel", "parallel unsequenced", "unsequenced".

```
std::vector<int> longVector;
// Find element using parallel execution policy
auto result1 = std::find(std::execution::par, std::begin(longVector), std::end(longVector), 2);
// Sort elements using sequential execution policy
auto result2 = std::sort(std::execution::seq, std::begin(longVector), std::end(longVector));

```

注釋
--

1. ↑   (PDF).  [2015-07-16]. （原始内容存档 (PDF)于2015-08-11）.
2. ↑ . 2014-05-06  [2015-07-16]. （原始内容存档于2018-07-09）.
3. ↑  IBM comment on preparing for a Trigraph-adverse future in C++17 （页面存档备份，存于）, IBM paper N4210, 2014-10-10. Authors: Michael Wong, Hubert Tong, Rajan Bhakta, Derek Inglis
4. ↑ .  [2015-07-16]. （原始内容存档于2015-08-11）.
5. ↑   (PDF).  [2015-07-16]. （原始内容 (PDF)存档于2014-11-29）.
6. 1 2 3 .  [2015-07-16]. （原始内容存档于2015-04-27）.
7. ↑ .  [2015-07-16]. （原始内容存档于2015-04-04）.
8. 1 2 3 .  [2015-07-16]. （原始内容存档于2014-11-29）.
9. ↑   (PDF).  [2015-07-16]. （原始内容存档 (PDF)于2015-03-09）.
10. ↑ .  [2015-07-16]. （原始内容存档于2014-11-29）.
11. ↑   (PDF).  [2016-08-02]. （原始内容存档 (PDF)于2016-04-05）.
12. ↑ .  [2016-08-02]. （原始内容存档于2016-04-05）.
13. ↑ .  [2015-07-16]. （原始内容存档于2015-08-10）.
14. 1 2 .  [2015-07-16]. （原始内容存档于2015-03-19）.
15. ↑ .  [2015-07-16]. （原始内容存档于2015-08-03）.
16. ↑ .  [2015-07-16]. （原始内容存档于2015-10-28）.
17. ↑ . en.cppreference.com.  [2016-06-23]. （原始内容存档于2016-06-17）.
18. ↑ .  [2016-08-02]. （原始内容存档于2016-07-20）.
19. ↑ .  [2015-07-16]. （原始内容存档于2015-04-27）.
20. ↑ .  [2016-08-02]. （原始内容存档于2016-04-05）.

另見
--

* C++
* C++14
* C++11

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
C++ |
| --- |
|  |
| 
* C++98
* C++03
* C++11
* C++14
* C++17
* C++20
* C++23

 |
|  |
| 特性 | 
* 类
* 异常处理
* 函数重载
* new 和 delete
* 运算符重载
* 运算符
* 模板

 |
|  |
| 标准库 | 
* I/O 流
* 智能指针
* STL
* 字符串

 |
|  |
| 想法 | 
* 奇异递归模板模式
* 最令人烦恼的解析
* RAII
* 三法則
* 替换失败并非错误

 |
|  |
| 编译器 | 
* Borland C++
* Borland Turbo C++
* C++Builder
* Clang
* GCC
* Intel C++编译器
* Sun Studio
* Visual C++
* Watcom C/C++编译器

 |
|  |
| IDE | 
* Anjuta
* CLion（英语：）
* Code::Blocks
* CodeLite（英语：）
* Eclipse
* Geany
* Microsoft Visual Studio
* NetBeans
* KDevelop
* Qt Creator

 |
|  |
| 方言 | 
* Embedded C++

 |
|  |
| 与其它语言的对照 | 
* C和C++的对照
* Java和C++的對照
* 编程语言的对照（英语：）

 |
|  |
| 
* 分类 **分类**

 |

 |