C++17

C++17 是C++標準委員會於2017年制定的標準，接替C++14標準，後被C++20標準取代。歷史.在C++標準委員會制定此次的三年期標準之前，C++17標準的釋出日期是不確定的。在這段日期中，C++17草案階段，並於同年12月完成最終標準釋出。17標準對C++語法進行了加強，並對標準模板庫進行了些許修改，如給庫中演算法增加了執行策略execution以支援平行計算。新功能.類模板的模板引數推導.模板類建構函式能自動推導模板引數的型別(Class template argument deduction, CTAD)。例如允許以codice_2 取代codice_3templatestruct MyContainer {T val;};MyContainer c1 {1}; // OK MyContainerMyContainer c2; // OK MyContainer任何指定變數或變數模板的初始化的宣告，其宣告的型別是類模板。如std::pair p(2, 4.5); // deduces to std::pair p(2, 4.5);new表示式，如template用auto宣告非型別模板引數.依據auto的推到規則，在可允許型別的非型別模板引數情況下，可從實參型別推匯出模板引數：templatestruct my_integer_sequence {// Implementation here ...// Explicitly pass type `int` as template argument.auto seq = std::integer_sequence();// Type is deduced to be `int`.auto seq2 = my_integer_sequence();摺疊表示式.摺疊表示式在運算子上執行模板引數包的展開計算。templatebool logicalAnd(Args... args) {// Binary folding.return (true &amp;&amp; ... &amp;&amp; args);bool b = true;bool&amp; b2 = b;logicalAnd(b, b2, true); // == truetemplateauto 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 elementauto x2 = {1, 2, 3}; // x2 is std::initializer_listauto x3 {3}; // x3 is intauto x4 {3.0}; // x4 is doubleconstexpr 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(); // 123valueRef(); // 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;Coordinate origin() {return Coordinate{0, 0};const auto [ x, y ] = origin();x; // == 0y; // == 0std::unordered_map 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 lk(mx);if (v.empty()) v.push_back(val);// vs.if (std::lock_guard 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：簡化模版偏特化的寫法templateconstexpr bool isIntegral() {if constexpr (std::is_integral::value) {return true;} else {return false;static_assert(isIntegral() == true);static_assert(isIntegral() == true);static_assert(isIntegral() == false);struct S {};static_assert(isIntegral() == false);//適用場景2：編寫變參模版函式templateint Sum()if constexpr(0 == sizeof...(Ns))return N;elsereturn N+Sum ();//使用場景3：替代enable_if。編寫模板函式時，經常要使用enable_if語句來進行靜態型別檢查，保證模板輸入的型別滿足某種要求templatebool IsOdd(T input){if constexpr (std::is_integral::value) //return static_cast(input % 2);utf8字面量.如下：char x = u8'x';列舉型別變數直接用大括號初始化.enum byte : unsigned char {};byte b {0}; // OKbyte c {-1}; // ERRORbyte d = byte{1}; // OKbyte e = byte{256}; // ERRORfallthrough, nodiscard, maybe_unused特性.C++17 引入了3個特性: fallthrough, nodiscard, maybe_unused.//fallthrough指示編譯器直通一個switch語句：switch (n) {case 1:fallthroughcase 2:break;//nodiscard引發一個警告，當具有該特性的函式或類的返回值被忽略：nodiscard bool do_something() {return is_success; // true for success, false for failuredo_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模板類實現了型別安全的unionstd::variant v{ 12 };std::get(v); // == 12std::get(v); // == 12v = 12.0;std::get(v); // == 12.0std::get(v); // == 12.0std::optional.std::optional類模板可能包含一個值或者可能不包含值：std::optional create(bool b) {if (b) {return "Godzilla";} else {return {};//或者 return std::nullopt;create(false).has_value();//返回true或falsecreate(false).value_or("empty"); // == "empty"create(true).value(); // == "Godzilla"// optional-returning factory functions are usable as conditions of while and ifif (auto str = create(true)) {std::any.std::any模板類是型別安全的包含任何型別的值：std::any x {5};x.has_value() // == truestd::any_cast(x) // == 5std::any_cast(x) = 10;std::any_cast(x) // == 10std::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用於呼叫“可呼叫物件”。templateclass Proxy {Callable c;public:templatedecltype(auto) operator()(Args&amp;&amp;... args) {return std::invoke(c, std::forward(args)...);};auto add = [](int x, int y) {return x + y;Proxy p {add};p(1, 2); // == 3std::apply.std::apply把std::tuple引數應用於可呼叫物件：auto add = [](int x, int y) {return x + y;std::apply(add, std::make_tuple(1, 2)); // == 3std::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(b); // 0xFFstd::byte c = a &amp; b;int j = std::to_integer(c); // 0maps 和 sets 上更有效率地移動節點.零開銷的節點操作：//Moving elements from one map to another:std::map src ;std::map 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 src {1, 3, 5};std::set dst {2, 4, 5};dst.merge(src);//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 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 longVector;// Find element using parallel execution policyauto result1 = std::find(std::execution::par, std::begin(longVector), std::end(longVector), 2);// Sort elements using sequential execution policyauto result2 = std::sort(std::execution::seq, std::begin(longVector), std::end(longVector));