C++17

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++语法进行了加强，并对标准模板库进行了些许修改，如给&lt;algorithm&gt;库中算法增加了执行策略execution以支持并行计算。
新功能.
新的语言特性.
类模板的模板参数推导.
模板类构造函数能自动推导模板参数的类型(Class template argument deduction, CTAD)。例如允许以codice_2 取代codice_3
template &lt;typename T = float&gt;
struct MyContainer {
 T val;
};
MyContainer c1 {1}; // OK MyContainer&lt;int&gt;
MyContainer c2; // OK MyContainer&lt;float&gt;
任何指定变量或变量模板的初始化的声明，其声明的类型是类模板（可能是 cv 限定的）。如std::pair p(2, 4.5); // deduces to std::pair&lt;int, double&gt; p(2, 4.5);
new表达式，如template&lt;class T&gt;struct A{ A(T, T);}; auto y = new A{1, 2}; // allocated type is A&lt;int&gt;
用auto声明非类型模板参数.
依据auto的推到规则，在可允许类型的非类型模板参数情况下，可从实参类型推导出模板参数：
template &lt;auto... seq&gt;
struct my_integer_sequence {
 // Implementation here ...
// Explicitly pass type `int` as template argument.
auto seq = std::integer_sequence&lt;int, 0, 1, 2&gt;();
// Type is deduced to be `int`.
auto seq2 = my_integer_sequence&lt;0, 1, 2&gt;();
折叠表达式.
折叠表达式在运算符上执行模板参数包的展开计算。
template &lt;typename... Args&gt;
bool logicalAnd(Args... args) {
 // Binary folding.
 return (true &amp;&amp; ... &amp;&amp; args);
bool b = true;
bool&amp; b2 = b;
logicalAnd(b, b2, true); // == true
template &lt;typename... Args&gt;
auto sum(Args... args) {
 // Unary folding.
 return (... + args);
sum(1.0, 2.0f, 3); // == 6.0
大括号初始化列表的新的自动推导规则.
以前codice_4 将被推导为codice_5, 现在推导为codice_6。
auto x1 {1, 2, 3}; // error: not a single element
auto x2 = {1, 2, 3}; // x2 is std::initializer_list&lt;int&gt;
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 {
 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;
结构化绑定.
变量定义初始化时，允许形如codice_7，其中expr的“元组类似的对象”包括codice_8等聚合结构。
using Coordinate = std::pair&lt;int, int&gt;;
Coordinate origin() {
 return Coordinate{0, 0};
const auto [ x, y ] = origin();
x; // == 0
y; // == 0
std::unordered_map&lt;std::string, int&gt; mapping {
 {"a", 1},
 {"b", 2},
// Destructure by reference.
for (const auto&amp; [key, value] : mapping) {
 // Do something with key and value
带初始化的选择语句.
if和switch语句允许如下的变量声明和初始化：
 std::lock_guard&lt;std::mutex&gt; lk(mx);
 if (v.empty()) v.push_back(val);
// vs.
if (std::lock_guard&lt;std::mutex&gt; 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 &lt;typename T&gt;
constexpr bool isIntegral() {
 if constexpr (std::is_integral&lt;T&gt;::value) {
 return true;
 } else {
 return false;
static_assert(isIntegral&lt;int&gt;() == true);
static_assert(isIntegral&lt;char&gt;() == true);
static_assert(isIntegral&lt;double&gt;() == false);
struct S {};
static_assert(isIntegral&lt;S&gt;() == false);
//适用场景2：编写变参模版函数
template&lt;int N, int...Ns&gt;
int Sum()
 if constexpr(0 == sizeof...(Ns))
 return N;
 else
 return N+Sum&lt;Ns...&gt; ();
//使用场景3：替代enable_if。编写模板函数时，经常要使用enable_if语句来进行静态类型检查，保证模板输入的类型满足某种要求
template &lt;typename T&gt;
bool IsOdd(T input){
 if constexpr (std::is_integral&lt;T&gt;::value) //
 return static_cast&lt;bool&gt;(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头文件：
//也可以用在不同位置、不同名字但具有相同地位的头文件的包含：
新的库特性.
std::variant.
std::variant模板类实现了类型安全的union
std::variant&lt;int, double&gt; v{ 12 };
std::get&lt;int&gt;(v); // == 12
std::get&lt;0&gt;(v); // == 12
v = 12.0;
std::get&lt;double&gt;(v); // == 12.0
std::get&lt;1&gt;(v); // == 12.0
std::optional.
std::optional类模板可能包含一个值或者可能不包含值：
std::optional&lt;std::string&gt; create(bool b) {
 if (b) {
 return "Godzilla";
 } else {
 return {};//或者 return std::nullopt;
create(false).has_value();//返回true或false
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&lt;int&gt;(x) // == 5
std::any_cast&lt;int&amp;&gt;(x) = 10;
std::any_cast&lt;int&gt;(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 &lt;typename Callable&gt;
class Proxy {
 Callable c;
public:
 template &lt;class... Args&gt;
 decltype(auto) operator()(Args&amp;&amp;... args) {
 return std::invoke(c, std::forward&lt;Args&gt;(args)...);
};
auto add = [](int x, int y) {
 return x + y;
Proxy&lt;decltype(add)&gt; 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 &gt; 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&lt;int&gt;(b); // 0xFF
std::byte c = a &amp; b;
int j = std::to_integer&lt;int&gt;(c); // 0
maps 和 sets 上更有效率地移动节点.
零开销的节点操作：
//Moving elements from one map to another:
std::map&lt;int, string&gt; src ;
std::map&lt;int, string&gt; dst ;
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&lt;int&gt; src {1, 3, 5};
std::set&lt;int&gt; dst {2, 4, 5};
dst.merge(src);
//Inserting elements which outlive the container:
auto elementFactory() {
 std::set&lt;...&gt; s;
 s.emplace(...);
 return s.extract(s.begin());
s2.insert(elementFactory());
//Changing the key of a map element:
std::map&lt;int, string&gt; m ;
auto e = m.extract(2);
e.key() = 4;
m.insert(std::move(e));
并行算法.
许多STL算法，如copy, find 和 sort，支持并行执行政策：seq, par, par_unseq, unseq分别表示"sequentially", "parallel", "parallel unsequenced", "unsequenced".
std::vector&lt;int&gt; 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));