# C++17 ## Overview Many of these descriptions and examples come from various resources (see [Acknowledgements](#acknowledgements) section), summarized in my own words. C++17 includes the following new language features: - [template argument deduction for class templates](#template-argument-deduction-for-class-templates) - [declaring non-type template parameters with auto](#declaring-non-type-template-parameters-with-auto) - [folding expressions](#folding-expressions) - [new rules for auto deduction from braced-init-list](#new-rules-for-auto-deduction-from-braced-init-list) - [constexpr lambda](#constexpr-lambda) - [lambda capture this by value](#lambda-capture-this-by-value) - [inline variables](#inline-variables) - [nested namespaces](#nested-namespaces) - [structured bindings](#structured-bindings) - [selection statements with initializer](#selection-statements-with-initializer) - [constexpr if](#constexpr-if) - [utf-8 character literals](#utf-8-character-literals) - [direct-list-initialization of enums](#direct-list-initialization-of-enums) C++17 includes the following new library features: - [std::variant](#stdvariant) - [std::optional](#stdoptional) - [std::any](#stdany) - [std::string_view](#stdstring_view) - [std::invoke](#stdinvoke) - [std::apply](#stdapply) - [splicing for maps and sets](#splicing-for-maps-and-sets) - [std::filesystem](#stdfilesystem) ## C++17 Language Features ### Template argument deduction for class templates Automatic template argument deduction much like how it's done for functions, but now including class constructors. ```c++ template struct MyContainer { T val; MyContainer() : val() {} MyContainer(T val) : val(val) {} // ... }; MyContainer c1{ 1 }; // OK MyContainer MyContainer c2; // OK MyContainer ``` ### Declaring non-type template parameters with auto Following the deduction rules of `auto`, while respecting the non-type template parameter list of allowable types[\*], template arguments can be deduced from the types of its arguments: ```c++ template struct 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<0, 1, 2>(); ``` \* - For example, you cannot use a `double` as a template parameter type, which also makes this an invalid deduction using `auto`. ### Folding expressions A fold expression performs a fold of a template parameter pack over a binary operator. * An expression of the form `(... op e)` or `(e op ...)`, where `op` is a fold-operator and `e` is an unexpanded parameter pack, are called _unary folds_. * An expression of the form `(e1 op ... op e2)`, where `op` are fold-operators, is called a _binary fold_. Either `e1` or `e2` are unexpanded parameter packs, but not both. ```c++ template bool logicalAnd(Args... args) { // Binary folding. return (true && ... && args); } bool b = true; bool& b2 = b; logicalAnd(b, b2, true); // == true ``` ```c++ template auto sum(Args... args) { // Unary folding. return (... + args); } sum(1.0, 2.0f, 3); // == 6.0 ``` ### New rules for auto deduction from braced-init-list Changes to `auto` deduction when used with the uniform initialization syntax. Previously, `auto x{ 3 };` deduces a `std::initializer_list`, which now deduces to `int`. ```c++ auto x1{ 1, 2, 3 }; // error: not a single element auto x2 = { 1, 2, 3 }; // decltype(x2) is std::initializer_list auto x3{ 3 }; // decltype(x3) is int auto x4{ 3.0 }; // decltype(x4) is double ``` ### constexpr lambda Compile-time lambdas using `constexpr`. ```c++ auto identity = [] (int n) constexpr { return n; }; static_assert(identity(123) == 123); ``` ```c++ 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); ``` ```c++ constexpr int addOne(int n) { return [n] { return n + 1; }(); } static_assert(addOne(1) == 2); ``` ### Lambda capture `this` by value Capturing `this` in a lambda's environment was previously reference-only. An example of where this is problematic is asynchronous code using callbacks that require an object to be available, potentially past its lifetime. `*this` (C++17) will now make a copy of the current object, while `this` (C++11) continues to capture by reference. ```c++ 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 variables The inline specifier can be applied to variables as well as to functions. A variable declared inline has the same semantics as a function declared inline. ```c++ // Disassembly example using compiler explorer. struct S { int x; }; inline S x1 = S{321}; // mov esi, dword ptr [x1] // x1: .long 321 S x2 = S{123}; // mov eax, dword ptr [.L_ZZ4mainE2x2] // mov dword ptr [rbp - 8], eax // .L_ZZ4mainE2x2: .long 123 ``` ### Nested namespaces Using the namespace resolution operator to create nested namespace definitions. ```c++ namespace A { namespace B { namespace C { int i; } } } // vs. namespace A::B::C { int i; } ``` ### Structured bindings A proposal for de-structuring initialization, that would allow writing `auto [ x, y, z ] = expr;` where the type of `expr` was a tuple-like object, whose elements would be bound to the variables `x`, `y`, and `z` (which this construct declares). _Tuple-like objects_ include `std::tuple`, `std::pair`, `std::array`, and aggregate structures. ```c++ using Coordinate = std::pair; Coordinate origin() { return Coordinate{0, 0}; } const auto [ x, y ] = origin(); x; // == 0 y; // == 0 ``` ### Selection statements with initializer New versions of the `if` and `switch` statements which simplify common code patterns and help users keep scopes tight. ```c++ { 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); } ``` ```c++ 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 Write code that is instantiated depending on a compile-time condition. ```c++ template constexpr 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); ``` ### UTF-8 Character Literals A character literal that begins with `u8` is a character literal of type `char`. The value of a UTF-8 character literal is equal to its ISO 10646 code point value. ```c++ char x = u8'x'; ``` ### Direct List Initialization of Enums Enums can now be initialized using braced syntax. ```c++ enum byte : unsigned char {}; byte b{0}; // OK byte c{-1}; // ERROR byte d = byte{1}; // OK byte e = byte{256}; // ERROR ``` ## C++17 Library Features ### std::variant The class template `std::variant` represents a type-safe `union`. An instance of `std::variant` at any given time holds a value of one of its alternative types (it's also possible for it to be valueless). ```c++ std::variant v{ 12 }; std::get(v); // == 12 std::get<0>(v); // == 12 v = 12.0; std::get(v); // == 12.0 std::get<1>(v); // == 12.0 ``` ### std::optional The class template `std::optional` manages an optional contained value, i.e. a value that may or may not be present. A common use case for optional is the return value of a function that may fail. ```c++ std::optional create(bool b) { if (b) { return "Godzilla"; } else { return {}; } } 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 A type-safe container for single values of any type. ```c++ std::any x{ 5 }; x.has_value() // == true std::any_cast(x) // == 5 std::any_cast(x) = 10; std::any_cast(x) // == 10 ``` ### std::string_view A non-owning reference to a string. Useful for providing an abstraction on top of strings (e.g. for parsing). ```c++ // 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, sizeof array); ``` ```c++ 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::invoke Invoke a `Callable` object with parameters. Examples of `Callable` objects are `std::function` or `std::bind` where an object can be called similarly to a regular function. ```c++ template class Proxy { Callable c; public: Proxy(Callable c): c(c) {} template decltype(auto) operator()(Args&&... args) { // ... return std::invoke(c, std::forward(args)...); } }; auto add = [] (int x, int y) { return x + y; }; Proxy p{ add }; p(1, 2); // == 3 ``` ### std::apply Invoke a `Callable` object with a tuple of arguments. ```c++ auto add = [] (int x, int y) { return x + y; }; std::apply(add, std::make_tuple( 1, 2 )); // == 3 ``` ### Splicing for maps and sets Moving nodes and merging containers without the overhead of expensive copies, moves, or heap allocations/deallocations. Moving elements from one map to another: ```c++ std::map src{ { 1, "one" }, { 2, "two" }, { 3, "buckle my shoe" } }; std::map 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: ```c++ std::set src{1, 3, 5}; std::set dst{2, 4, 5}; dst.merge(src); // src == { 5 } // dst == { 1, 2, 3, 4, 5 } ``` Inserting elements which outlive the container: ```c++ auto elementFactory() { std::set<...> s; s.emplace(...); return s.extract(s.begin()); } s2.insert(elementFactory()); ``` Changing the key of a map element: ```c++ std::map 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" } } ``` ### std::filesystem The new `std::filesystem` library provides a standard way to manipulate files, directories, and paths in a filesystem. Here, a big file is copied to a temporary path if there is available space: ```c++ 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")); } } ``` ## Acknowledgements * [cppreference](http://en.cppreference.com/w/cpp) - especially useful for finding examples and documentation of new library features. * [C++ Rvalue References Explained](http://thbecker.net/articles/rvalue_references/section_01.html) - a great introduction I used to understand rvalue references, perfect forwarding, and move semantics. * [clang](http://clang.llvm.org/cxx_status.html) and [gcc](https://gcc.gnu.org/projects/cxx-status.html)'s standards support pages. Also included here are the proposals for language/library features that I used to help find a description of, what it's meant to fix, and some examples. * [Compiler explorer](https://godbolt.org/) * [Scott Meyers' Effective Modern C++](https://www.amazon.com/Effective-Modern-Specific-Ways-Improve/dp/1491903996) - highly recommended book! * [Jason Turner's C++ Weekly](https://www.youtube.com/channel/UCxHAlbZQNFU2LgEtiqd2Maw) - nice collection of C++-related videos. * [What can I do with a moved-from object?](http://stackoverflow.com/questions/7027523/what-can-i-do-with-a-moved-from-object) * [What are some uses of decltype(auto)?](http://stackoverflow.com/questions/24109737/what-are-some-uses-of-decltypeauto) * And many more SO posts I'm forgetting... ## Author Anthony Calandra ## Content Contributors See: https://github.com/AnthonyCalandra/modern-cpp-features/graphs/contributors ## License MIT