diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2fbbc41..4403694 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,11 @@ I'm not very picky about how you should contribute, but I ask that the following * Proper spelling and grammar. * If it's a language or library feature that you can write code with, please provide an example of its usage. An optimal submission would also include a short real-world use case for the feature. -* Make sure the feature is in the correct C++ version. +* Keep additions/deletions of content consistent with the cheatsheet's goals (see below). + +#### Instructions +* Make sure the feature is in the correct C++ version file (i.e. CPP11.md, etc.). * Make sure you've added the feature to the table of contents. -* Make sure you have also added the feature to the separate major C++ readme files (ie. CPP11.md, CPP14.md, etc.). + +## Goals +My goal for this cheatsheet is to prefer conciseness over absolute completeness. Examples of features should be minimal: if an example is overly complicated, large, or is more of an obscure usage of the feature then it will most likely be rejected in review. The reason for this goal is to teach users what the most popular uses of these features will be, and for a more thorough investigation, to learn about those from external C++ resources. diff --git a/CPP11.md b/CPP11.md index 431eb57..0d92bc1 100644 --- a/CPP11.md +++ b/CPP11.md @@ -1,18 +1,19 @@ # C++11 ## Overview -Many of these descriptions and examples come from various resources (see [Acknowledgements](#acknowledgements) section), summarized in my own words. +Many of these descriptions and examples are taken from various resources (see [Acknowledgements](#acknowledgements) section) and summarized in my own words. C++11 includes the following new language features: - [move semantics](#move-semantics) - [variadic templates](#variadic-templates) - [rvalue references](#rvalue-references) +- [forwarding references](#forwarding-references) - [initializer lists](#initializer-lists) - [static assertions](#static-assertions) - [auto](#auto) - [lambda expressions](#lambda-expressions) - [decltype](#decltype) -- [template aliases](#template-aliases) +- [type aliases](#type-aliases) - [nullptr](#nullptr) - [strongly-typed enums](#strongly-typed-enums) - [attributes](#attributes) @@ -30,10 +31,16 @@ C++11 includes the following new language features: - [inline-namespaces](#inline-namespaces) - [non-static data member initializers](#non-static-data-member-initializers) - [right angle brackets](#right-angle-brackets) +- [ref-qualified member functions](#ref-qualified-member-functions) +- [trailing return types](#trailing-return-types) +- [noexcept specifier](#noexcept-specifier) +- [char32_t and char16_t](#char32_t-and-char16_t) +- [raw string literals](#raw-string-literals) C++11 includes the following new library features: - [std::move](#stdmove) - [std::forward](#stdforward) +- [std::thread](#stdthread) - [std::to_string](#stdto_string) - [type traits](#type-traits) - [smart pointers](#smart-pointers) @@ -43,34 +50,88 @@ C++11 includes the following new library features: - [std::array](#stdarray) - [unordered containers](#unordered-containers) - [std::make_shared](#stdmake_shared) +- [std::ref](#stdref) - [memory model](#memory-model) +- [std::async](#stdasync) +- [std::begin/end](#stdbeginend) ## C++11 Language Features ### Move semantics -Move semantics is mostly about performance optimization: the ability to move an object without the expensive overhead of copying. The difference between a copy and a move is that a copy leaves the source unchanged, and a move will leave the source either unchanged or radically different -- depending on what the source is. For plain old data, a move is the same as a copy. +Moving an object means to transfer ownership of some resource it manages to another object. -To move an object means to transfer ownership of some resource it manages to another object. You could think of this as changing pointers held by the source object to be moved, or now held, by the destination object; the resource remains in its location in memory. Such an inexpensive transfer of resources is extremely useful when the source is an `rvalue`, where the potentially dangerous side-effect of changing the source after the move is redundant since the source is a temporary object that won't be accessible later. +The first benefit of move semantics is performance optimization. When an object is about to reach the end of its lifetime, either because it's a temporary or by explicitly calling `std::move`, a move is often a cheaper way to transfer resources. For example, moving a `std::vector` is just copying some pointers and internal state over to the new vector -- copying would involve having to copy every single contained element in the vector, which is expensive and unnecessary if the old vector will soon be destroyed. -Moves also make it possible to transfer objects such as `std::unique_ptr`s, [smart pointers](#smart-pointers) that are designed to hold a pointer to a unique object, from one scope to another. +Moves also make it possible for non-copyable types such as `std::unique_ptr`s ([smart pointers](#smart-pointers)) to guarantee at the language level that there is only ever one instance of a resource being managed at a time, while being able to transfer an instance between scopes. -See the sections on: [rvalue references](#rvalue-references), [defining move special member functions](#special-member-functions-for-move-semantics), [`std::move`](#stdmove), [`std::forward`](#stdforward). +See the sections on: [rvalue references](#rvalue-references), [special member functions for move semantics](#special-member-functions-for-move-semantics), [`std::move`](#stdmove), [`std::forward`](#stdforward), [`forwarding references`](#forwarding-references). ### Rvalue references -C++11 introduces a new reference termed the _rvalue reference_. An rvalue reference to `A` is created with the syntax `A&&`. This enables two major features: move semantics; and _perfect forwarding_, the ability to pass arguments while maintaining information about them as lvalues/rvalues in a generic way. +C++11 introduces a new reference termed the _rvalue reference_. An rvalue reference to `T`, which is a non-template type parameter (such as `int`, or a user-defined type), is created with the syntax `T&&`. Rvalue references only bind to rvalues. -`auto` type deduction with lvalues and rvalues: +Type deduction with lvalues and rvalues: ```c++ int x = 0; // `x` is an lvalue of type `int` int& xl = x; // `xl` is an lvalue of type `int&` int&& xr = x; // compiler error -- `x` is an lvalue -int&& xr2 = 0; // `xr2` is an lvalue of type `int&&` -auto& al = x; // `al` is an lvalue of type `int&` -auto&& al2 = x; // `al2` is an lvalue of type `int&` -auto&& ar = 0; // `ar` is an lvalue of type `int&&` +int&& xr2 = 0; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0` + +void f(int& x) {} +void f(int&& x) {} + +f(x); // calls f(int&) +f(xl); // calls f(int&) +f(3); // calls f(int&&) +f(std::move(x)); // calls f(int&&) + +f(xr2); // calls f(int&) +f(std::move(xr2)); // calls f(int&& x) +``` + +See also: [`std::move`](#stdmove), [`std::forward`](#stdforward), [`forwarding references`](#forwarding-references). + +### Forwarding references +Also known (unofficially) as _universal references_. A forwarding reference is created with the syntax `T&&` where `T` is a template type parameter, or using `auto&&`. This enables _perfect forwarding_: the ability to pass arguments while maintaining their value category (e.g. lvalues stay as lvalues, temporaries are forwarded as rvalues). + +Forwarding references allow a reference to bind to either an lvalue or rvalue depending on the type. Forwarding references follow the rules of _reference collapsing_: +* `T& &` becomes `T&` +* `T& &&` becomes `T&` +* `T&& &` becomes `T&` +* `T&& &&` becomes `T&&` + +`auto` type deduction with lvalues and rvalues: +```c++ +int x = 0; // `x` is an lvalue of type `int` +auto&& al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x` +auto&& ar = 0; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0` ``` -See also: [`std::move`](#stdmove), [`std::forward`](#stdforward). +Template type parameter deduction with lvalues and rvalues: +```c++ +// Since C++14 or later: +void f(auto&& t) { + // ... +} + +// Since C++11 or later: +template +void f(T&& t) { + // ... +} + +int x = 0; +f(0); // T is int, deduces as f(int &&) => f(int&&) +f(x); // T is int&, deduces as f(int& &&) => f(int&) + +int& y = x; +f(y); // T is int&, deduces as f(int& &&) => f(int&) + +int&& z = 0; // NOTE: `z` is an lvalue with type `int&&`. +f(z); // T is int&, deduces as f(int& &&) => f(int&) +f(std::move(z)); // T is int, deduces as f(int &&) => f(int&&) +``` + +See also: [`std::move`](#stdmove), [`std::forward`](#stdforward), [`rvalue references`](#rvalue-references). ### Variadic templates The `...` syntax creates a _parameter pack_ or expands one. A template _parameter pack_ is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A template with at least one parameter pack is called a _variadic template_. @@ -83,6 +144,19 @@ static_assert(arity<>::value == 0); static_assert(arity::value == 3); ``` +An interesting use for this is creating an _initializer list_ from a _parameter pack_ in order to iterate over variadic function arguments. +```c++ +template +auto sum(const First first, const Args... args) -> decltype(first) { + const auto values = {first, args...}; + return std::accumulate(values.begin(), values.end(), First{0}); +} + +sum(1, 2, 3, 4, 5); // 15 +sum(1, 2, 3); // 6 +sum(1.5, 2.0, 3.7); // 7.2 +``` + ### Initializer lists A lightweight array-like container of elements created using a "braced list" syntax. For example, `{ 1, 2, 3 }` creates a sequences of integers, that has type `std::initializer_list`. Useful as a replacement to passing a vector of objects to a function. ```c++ @@ -95,9 +169,9 @@ int sum(const std::initializer_list& list) { return total; } -auto list = { 1, 2, 3 }; +auto list = {1, 2, 3}; sum(list); // == 6 -sum({ 1, 2, 3 }); // == 6 +sum({1, 2, 3}); // == 6 sum({}); // == 0 ``` @@ -150,13 +224,13 @@ A `lambda` is an unnamed function object capable of capturing variables in scope * `[]` - captures nothing. * `[=]` - capture local objects (local variables, parameters) in scope by value. * `[&]` - capture local objects (local variables, parameters) in scope by reference. -* `[this]` - capture `this` pointer by value. +* `[this]` - capture `this` by reference. * `[a, &b]` - capture objects `a` by value, `b` by reference. ```c++ int x = 1; -auto getX = [=]{ return x; }; +auto getX = [=] { return x; }; getX(); // == 1 auto addX = [=](int y) { return x + y; }; @@ -173,11 +247,11 @@ auto f1 = [&x] { x = 2; }; // OK: x is a reference and modifies the original auto f2 = [x] { x = 2; }; // ERROR: the lambda can only perform const-operations on the captured value // vs. -auto f3 = [x] () mutable { x = 2; }; // OK: the lambda can perform any operations on the captured value +auto f3 = [x]() mutable { x = 2; }; // OK: the lambda can perform any operations on the captured value ``` ### decltype -`decltype` is an operator which returns the _declared type_ of an expression passed to it. Examples of `decltype`: +`decltype` is an operator which returns the _declared type_ of an expression passed to it. cv-qualifiers and references are maintained if they are part of the expression. Examples of `decltype`: ```c++ int a = 1; // `a` is declared as type `int` decltype(a) b = a; // `decltype(a)` is `int` @@ -196,15 +270,17 @@ auto add(X x, Y y) -> decltype(x + y) { add(1, 2.0); // `decltype(x + y)` => `decltype(3.0)` => `double` ``` -### Template aliases -Semantically similar to using a `typedef` however, template aliases with `using` are easier to read and are compatible with templates. +See also: [`decltype(auto) (C++14)`](README.md#decltypeauto). + +### Type aliases +Semantically similar to using a `typedef` however, type aliases with `using` are easier to read and are compatible with templates. ```c++ template using Vec = std::vector; -Vec v{}; // std::vector +Vec v; // std::vector using String = std::string; -String s{"foo"}; +String s {"foo"}; ``` ### nullptr @@ -236,7 +312,7 @@ Attributes provide a universal syntax over `__attribute__(...)`, `__declspec`, e ``` ### constexpr -Constant expressions are expressions evaluated by the compiler at compile-time. Only non-complex computations can be carried out in a constant expression. Use the `constexpr` specifier to indicate the variable, function, etc. is a constant expression. +Constant expressions are expressions that are *possibly* evaluated by the compiler at compile-time. Only non-complex computations can be carried out in a constant expression (these rules are progressively relaxed in later versions). Use the `constexpr` specifier to indicate the variable, function, etc. is a constant expression. ```c++ constexpr int square(int x) { return x * x; @@ -252,8 +328,9 @@ int b = square2(2); // mov edi, 2 // call square2(int) // mov DWORD PTR [rbp-8], eax ``` +In the previous snippet, notice that the computation when calling `square` is carried out at compile-time, and then the result is embedded in the code generation, while `square2` is called at run-time. -`constexpr` values are those that the compiler can evaluate at compile-time: +`constexpr` values are those that the compiler can evaluate, but are not guaranteed to, at compile-time: ```c++ const int x = 123; constexpr const int& y = x; // error -- constexpr variable `y` must be initialized by a constant expression @@ -262,7 +339,7 @@ constexpr const int& y = x; // error -- constexpr variable `y` must be initializ Constant expressions with classes: ```c++ struct Complex { - constexpr Complex(double r, double i) : re(r), im(i) { } + constexpr Complex(double r, double i) : re{r}, im{i} { } constexpr double real() { return re; } constexpr double imag() { return im; } @@ -279,11 +356,11 @@ Constructors can now call other constructors in the same class using an initiali ```c++ struct Foo { int foo; - Foo(int foo) : foo(foo) {} + Foo(int foo) : foo{foo} {} Foo() : Foo(0) {} }; -Foo foo{}; +Foo foo; foo.foo; // == 0 ``` @@ -342,13 +419,8 @@ struct C : B { Class cannot be inherited from. ```c++ -struct A final { - -}; - -struct B : A { // error -- base 'A' is marked 'final' - -}; +struct A final {}; +struct B : A {}; // error -- base 'A' is marked 'final' ``` ### Default functions @@ -356,17 +428,17 @@ A more elegant, efficient way to provide a default implementation of a function, ```c++ struct A { A() = default; - A(int x) : x(x) {} - int x{ 1 }; + A(int x) : x{x} {} + int x {1}; }; -A a{}; // a.x == 1 -A a2{ 123 }; // a.x == 123 +A a; // a.x == 1 +A a2 {123}; // a.x == 123 ``` With inheritance: ```c++ struct B { - B() : x(1); + B() : x{1} {} int x; }; @@ -375,7 +447,7 @@ struct C : B { C() = default; }; -C c{}; // c.x == 1 +C c; // c.x == 1 ``` ### Deleted functions @@ -385,12 +457,12 @@ class A { int x; public: - A(int x) : x(x) {}; + A(int x) : x{x} {}; A(const A&) = delete; A& operator=(const A&) = delete; }; -A x{ 123 }; +A x {123}; A y = x; // error -- call to deleted copy constructor y = x; // error -- operator= deleted ``` @@ -398,14 +470,14 @@ y = x; // error -- operator= deleted ### Range-based for loops Syntactic sugar for iterating over a container's elements. ```c++ -std::array a{ 1, 2, 3, 4, 5 }; +std::array a {1, 2, 3, 4, 5}; for (int& x : a) x *= 2; // a == { 2, 4, 6, 8, 10 } ``` Note the difference when using `int` as opposed to `int&`: ```c++ -std::array a{ 1, 2, 3, 4, 5 }; +std::array a {1, 2, 3, 4, 5}; for (int x : a) x *= 2; // a == { 1, 2, 3, 4, 5 } ``` @@ -415,9 +487,9 @@ The copy constructor and copy assignment operator are called when copies are mad ```c++ struct A { std::string s; - A() : s("test") {} - A(const A& o) : s(o.s) {} - A(A&& o) : s(std::move(o.s)) {} + A() : s{"test"} {} + A(const A& o) : s{o.s} {} + A(A&& o) : s{std::move(o.s)} {} A& operator=(A&& o) { s = std::move(o.s); return *this; @@ -444,10 +516,10 @@ struct A { A(int, int, int) {} }; -A a{0, 0}; // calls A::A(int, int) +A a {0, 0}; // calls A::A(int, int) A b(0, 0); // calls A::A(int, int) A c = {0, 0}; // calls A::A(int, int) -A d{0, 0, 0}; // calls A::A(int, int, int) +A d {0, 0, 0}; // calls A::A(int, int, int) ``` Note that the braced list syntax does not allow narrowing: @@ -457,7 +529,7 @@ struct A { }; A a(1.1); // OK -A b{1.1}; // Error narrowing conversion from double to int +A b {1.1}; // Error narrowing conversion from double to int ``` Note that if a constructor accepts a `std::initializer_list`, it will be called instead: @@ -469,10 +541,10 @@ struct A { A(std::initializer_list) {} }; -A a{0, 0}; // calls A::A(std::initializer_list) +A a {0, 0}; // calls A::A(std::initializer_list) A b(0, 0); // calls A::A(int, int) A c = {0, 0}; // calls A::A(std::initializer_list) -A d{0, 0, 0}; // calls A::A(std::initializer_list) +A d {0, 0, 0}; // calls A::A(std::initializer_list) ``` ### Explicit conversion functions @@ -486,11 +558,11 @@ struct B { explicit operator bool() const { return true; } }; -A a{}; +A a; if (a); // OK calls A::operator bool() bool ba = a; // OK copy-initialization selects A::operator bool() -B b{}; +B b; if (b); // OK calls B::operator bool() bool bb = b; // error copy-initialization does not consider B::operator bool() ``` @@ -519,19 +591,18 @@ Allows non-static data members to be initialized where they are declared, potent ```c++ // Default initialization prior to C++11 class Human { - Human() : age(0) {} + Human() : age{0} {} private: unsigned age; }; // Default initialization on C++11 class Human { private: - unsigned age{0}; + unsigned age {0}; }; ``` - -### Right angle Brackets +### Right angle brackets C++11 is now able to infer when a series of right angle brackets is used as an operator or as a closing statement of typedef, without having to add whitespace. ```c++ @@ -539,12 +610,121 @@ typedef std::map > > cpp98LongTypedef; typedef std::map>> cpp11LongTypedef; ``` +### Ref-qualified member functions +Member functions can now be qualified depending on whether `*this` is an lvalue or rvalue reference. + +```c++ +struct Bar { + // ... +}; + +struct Foo { + Bar& getBar() & { return bar; } + const Bar& getBar() const& { return bar; } + Bar&& getBar() && { return std::move(bar); } + const Bar&& getBar() const&& { return std::move(bar); } +private: + Bar bar; +}; + +Foo foo{}; +Bar bar = foo.getBar(); // calls `Bar& getBar() &` + +const Foo foo2{}; +Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&` + +Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&` +std::move(foo).getBar(); // calls `Bar&& Foo::getBar() &&` +std::move(foo2).getBar(); // calls `const Bar&& Foo::getBar() const&` +``` + +### Trailing return types +C++11 allows functions and lambdas an alternative syntax for specifying their return types. +```c++ +int f() { + return 123; +} +// vs. +auto f() -> int { + return 123; +} +``` +```c++ +auto g = []() -> int { + return 123; +}; +``` +This feature is especially useful when certain return types cannot be resolved: +```c++ +// NOTE: This does not compile! +template +decltype(a + b) add(T a, U b) { + return a + b; +} + +// Trailing return types allows this: +template +auto add(T a, U b) -> decltype(a + b) { + return a + b; +} +``` +In C++14, [`decltype(auto) (C++14)`](README.md#decltypeauto) can be used instead. + +### Noexcept specifier +The `noexcept` specifier specifies whether a function could throw exceptions. It is an improved version of `throw()`. + +```c++ +void func1() noexcept; // does not throw +void func2() noexcept(true); // does not throw +void func3() throw(); // does not throw + +void func4() noexcept(false); // may throw +``` + +Non-throwing functions are permitted to call potentially-throwing functions. Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function std::terminate is called. + +```c++ +extern void f(); // potentially-throwing +void g() noexcept { + f(); // valid, even if f throws + throw 42; // valid, effectively a call to std::terminate +} +``` + +### char32_t and char16_t +Provides standard types for representing UTF-8 strings. +```c++ +char32_t utf8_str[] = U"\u0123"; +char16_t utf8_str[] = u"\u0123"; +``` + +### Raw string literals +C++11 introduces a new way to declare string literals as "raw string literals". Characters issued from an escape sequence (tabs, line feeds, single backslashes, etc.) can be inputted raw while preserving formatting. This is useful, for example, to write literary text, which might contain a lot of quotes or special formatting. This can make your string literals easier to read and maintain. + +A raw string literal is declared using the following syntax: +``` +R"delimiter(raw_characters)delimiter" +``` +where: +* `delimiter` is an optional sequence of characters made of any source character except parentheses, backslashes and spaces. +* `raw_characters` is any raw character sequence; must not contain the closing sequence `")delimiter"`. + +Example: +```cpp +// msg1 and msg2 are equivalent. +const char* msg1 = "\nHello,\n\tworld!\n"; +const char* msg2 = R"( +Hello, + world! +)"; +``` + ## C++11 Library Features ### std::move -`std::move` indicates that the object passed to it may be moved, or in other words, moved from one object to another without a copy. The object passed in should not be used after the move in certain situations. +`std::move` indicates that the object passed to it may have its resources transferred. Using objects that have been moved from should be used with care, as they can be left in an unspecified state (see: [What can I do with a moved-from object?](http://stackoverflow.com/questions/7027523/what-can-i-do-with-a-moved-from-object)). -A definition of `std::move` (performing a move is nothing more than casting to an rvalue): +A definition of `std::move` (performing a move is nothing more than casting to an rvalue reference): ```c++ template typename remove_reference::type&& move(T&& arg) { @@ -554,18 +734,14 @@ typename remove_reference::type&& move(T&& arg) { Transferring `std::unique_ptr`s: ```c++ -std::unique_ptr p1{ new int }; +std::unique_ptr p1 {new int{0}}; // in practice, use std::make_unique std::unique_ptr p2 = p1; // error -- cannot copy unique pointers -std::unique_ptr p3 = std::move(p1); // move `p1` into `p2` +std::unique_ptr p3 = std::move(p1); // move `p1` into `p3` // now unsafe to dereference object held by `p1` ``` ### std::forward -Returns the arguments passed to it as-is, either as an lvalue or rvalue references, and includes cv-qualification. Useful for generic code that need a reference (either lvalue or rvalue) when appropriate, e.g factories. Forwarding gets its power from _template argument deduction_: -* `T& &` becomes `T&` -* `T& &&` becomes `T&` -* `T&& &` becomes `T&` -* `T&& &&` becomes `T&&` +Returns the arguments passed to it while maintaining their value category and cv-qualifiers. Useful for generic code and factories. Used in conjunction with [`forwarding references`](#forwarding-references). A definition of `std::forward`: ```c++ @@ -585,15 +761,33 @@ struct A { template A wrapper(T&& arg) { - return A{ std::forward(arg) }; + return A{std::forward(arg)}; } wrapper(A{}); // moved -A a{}; +A a; wrapper(a); // copied wrapper(std::move(a)); // moved ``` +See also: [`forwarding references`](#forwarding-references), [`rvalue references`](#rvalue-references). + +### std::thread +The `std::thread` library provides a standard way to control threads, such as spawning and killing them. In the example below, multiple threads are spawned to do different calculations and then the program waits for all of them to finish. + +```c++ +void foo(bool clause) { /* do something... */ } + +std::vector threadsVector; +threadsVector.emplace_back([]() { + // Lambda function that will be invoked +}); +threadsVector.emplace_back(foo, true); // thread will run foo(true) +for (auto& thread : threadsVector) { + thread.join(); // Wait for threads to finish +} +``` + ### std::to_string Converts a numeric argument to a `std::string`. ```c++ @@ -604,27 +798,31 @@ std::to_string(123); // == "123" ### Type traits Type traits defines a compile-time template-based interface to query or modify the properties of types. ```c++ -static_assert(std::is_integral::value == 1); -static_assert(std::is_same::value == 1); -static_assert(std::is_same::type, int>::value == 1); +static_assert(std::is_integral::value); +static_assert(std::is_same::value); +static_assert(std::is_same::type, int>::value); ``` ### Smart pointers -C++11 introduces new smart(er) pointers: `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr`. `std::auto_ptr` now becomes deprecated and then eventually removed in C++17. +C++11 introduces new smart pointers: `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr`. `std::auto_ptr` now becomes deprecated and then eventually removed in C++17. -`std::unique_ptr` is a non-copyable, movable smart pointer that properly manages arrays and STL containers. **Note: Prefer using the `std::make_X` helper functions as opposed to using constructors. See the sections for [std::make_unique](#stdmake_unique) and [std::make_shared](#stdmake_shared).** +`std::unique_ptr` is a non-copyable, movable pointer that manages its own heap-allocated memory. **Note: Prefer using the `std::make_X` helper functions as opposed to using constructors. See the sections for [std::make_unique](https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP14.md#stdmake_unique) and [std::make_shared](#stdmake_shared).** ```c++ std::unique_ptr p1 { new Foo{} }; // `p1` owns `Foo` -if (p1) p1->bar(); +if (p1) { + p1->bar(); +} { - std::unique_ptr p2 { std::move(p1) }; // Now `p2` owns `Foo` + std::unique_ptr p2 {std::move(p1)}; // Now `p2` owns `Foo` f(*p2); p1 = std::move(p2); // Ownership returns to `p1` -- `p2` gets destroyed } -if (p1) p1->bar(); +if (p1) { + p1->bar(); +} // `Foo` instance is destroyed when `p1` goes out of scope ``` @@ -642,7 +840,7 @@ void baz(std::shared_ptr t) { // Do something with `t`... } -std::shared_ptr p1 { new T{} }; +std::shared_ptr p1 {new T{}}; // Perhaps these take place in another threads? foo(p1); bar(p1); @@ -652,20 +850,19 @@ baz(p1); ### std::chrono The chrono library contains a set of utility functions and types that deal with _durations_, _clocks_, and _time points_. One use case of this library is benchmarking code: ```c++ -std::chrono::time_point start, end; -start = std::chrono::system_clock::now(); +std::chrono::time_point start, end; +start = std::chrono::steady_clock::now(); // Some computations... -end = std::chrono::system_clock::now(); - -std::chrono::duration elapsed_seconds = end-start; +end = std::chrono::steady_clock::now(); -elapsed_seconds.count(); // t number of seconds, represented as a `double` +std::chrono::duration elapsed_seconds = end - start; +double t = elapsed_seconds.count(); // t number of seconds, represented as a `double` ``` ### Tuples Tuples are a fixed-size collection of heterogeneous values. Access the elements of a `std::tuple` by unpacking using [`std::tie`](#stdtie), or using `std::get`. ```c++ -// `playerProfile` has type `std::tuple`. +// `playerProfile` has type `std::tuple`. auto playerProfile = std::make_tuple(51, "Frans Nielsen", "NYI"); std::get<0>(playerProfile); // 51 std::get<1>(playerProfile); // "Frans Nielsen" @@ -705,7 +902,7 @@ These containers maintain average constant-time complexity for search, insert, a * Prevents code repetition when specifying the underlying type the pointer shall hold. * It provides exception-safety. Suppose we were calling a function `foo` like so: ```c++ -foo(std::shared_ptr{ new T{} }, function_that_throws(), std::shared_ptr{ new T{} }); +foo(std::shared_ptr{new T{}}, function_that_throws(), std::shared_ptr{new T{}}); ``` The compiler is free to call `new T{}`, then `function_that_throws()`, and so on... Since we have allocated data on the heap in the first construction of a `T`, we have introduced a leak here. With `std::make_shared`, we are given exception-safety: ```c++ @@ -715,12 +912,66 @@ foo(std::make_shared(), function_that_throws(), std::make_shared()); See the section on [smart pointers](#smart-pointers) for more information on `std::unique_ptr` and `std::shared_ptr`. +### std::ref +`std::ref(val)` is used to create object of type `std::reference_wrapper` that holds reference of val. Used in cases when usual reference passing using `&` does not compile or `&` is dropped due to type deduction. `std::cref` is similar but created reference wrapper holds a const reference to val. + +```c++ +// create a container to store reference of objects. +auto val = 99; +auto _ref = std::ref(val); +_ref++; +auto _cref = std::cref(val); +//_cref++; does not compile +std::vector>vec; // vectorvec does not compile +vec.push_back(_ref); // vec.push_back(&i) does not compile +cout << val << endl; // prints 100 +cout << vec[0] << endl; // prints 100 +cout << _cref; // prints 100 +``` + ### Memory model C++11 introduces a memory model for C++, which means library support for threading and atomic operations. Some of these operations include (but aren't limited to) atomic loads/stores, compare-and-swap, atomic flags, promises, futures, locks, and condition variables. +See the sections on: [std::thread](#stdthread) + +### std::async +`std::async` runs the given function either asynchronously or lazily-evaluated, then returns a `std::future` which holds the result of that function call. + +The first parameter is the policy which can be: +1. `std::launch::async | std::launch::deferred` It is up to the implementation whether to perform asynchronous execution or lazy evaluation. +1. `std::launch::async` Run the callable object on a new thread. +1. `std::launch::deferred` Perform lazy evaluation on the current thread. + +```c++ +int foo() { + /* Do something here, then return the result. */ + return 1000; +} + +auto handle = std::async(std::launch::async, foo); // create an async task +auto result = handle.get(); // wait for the result +``` + +### std::begin/end +`std::begin` and `std::end` free functions were added to return begin and end iterators of a container generically. These functions also work with raw arrays which do not have `begin` and `end` member functions. + +```c++ +template +int CountTwos(const T& container) { + return std::count_if(std::begin(container), std::end(container), [](int item) { + return item == 2; + }); +} + +std::vector vec = {2, 2, 43, 435, 4543, 534}; +int arr[8] = {2, 43, 45, 435, 32, 32, 32, 32}; +auto a = CountTwos(vec); // 2 +auto b = CountTwos(arr); // 1 +``` + ## 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. +* [C++ Rvalue References Explained](http://web.archive.org/web/20240324121501/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! diff --git a/CPP14.md b/CPP14.md index 6e80c6a..f8ba613 100644 --- a/CPP14.md +++ b/CPP14.md @@ -1,7 +1,7 @@ # C++14 ## Overview -Many of these descriptions and examples come from various resources (see [Acknowledgements](#acknowledgements) section), summarized in my own words. +Many of these descriptions and examples are taken from various resources (see [Acknowledgements](#acknowledgements) section) and summarized in my own words. C++14 includes the following new language features: - [binary literals](#binary-literals) @@ -11,6 +11,7 @@ C++14 includes the following new language features: - [decltype(auto)](#decltypeauto) - [relaxing constraints on constexpr functions](#relaxing-constraints-on-constexpr-functions) - [variable templates](#variable-templates) +- [\[\[deprecated\]\] attribute](#deprecated-attribute) C++14 includes the following new library features: - [user-defined literals for standard library types](#user-defined-literals-for-standard-library-types) @@ -89,7 +90,7 @@ int& z = g(y); // reference to `y` ``` ### decltype(auto) -The `decltype(auto)` type-specifier also deduces a type like `auto` does. However, it deduces return types while keeping their references or "const-ness", while `auto` will not. +The `decltype(auto)` type-specifier also deduces a type like `auto` does. However, it deduces return types while keeping their references and cv-qualifiers, while `auto` will not. ```c++ const int x = 0; auto x1 = x; // int @@ -121,6 +122,8 @@ static_assert(std::is_same::value == 1); static_assert(std::is_same::value == 1); ``` +See also: [`decltype (C++11)`](README.md#decltype). + ### Relaxing constraints on constexpr functions In C++11, `constexpr` function bodies could only contain a very limited set of syntaxes, including (but not limited to): `typedef`s, `using`s, and a single `return` statement. In C++14, the set of allowable syntaxes expands greatly to include the most common syntax such as `if` statements, multiple `return`s, loops, etc. ```c++ @@ -134,7 +137,7 @@ constexpr int factorial(int n) { factorial(5); // == 120 ``` -### Variable Templates +### Variable templates C++14 allows variables to be templated: ```c++ @@ -144,6 +147,15 @@ template constexpr T e = T(2.7182818284590452353); ``` +### [[deprecated]] attribute +C++14 introduces the `[[deprecated]]` attribute to indicate that a unit (function, class, etc.) is discouraged and likely yield compilation warnings. If a reason is provided, it will be included in the warnings. +```c++ +[[deprecated]] +void old_method(); +[[deprecated("Use new_method instead")]] +void legacy_method(); +``` + ## C++14 Library Features ### User-defined literals for standard library types @@ -157,7 +169,7 @@ std::chrono::duration_cast(day).count(); // == 1440 ### Compile-time integer sequences The class template `std::integer_sequence` represents a compile-time sequence of integers. There are a few helpers built on top: -* `std::make_integer_sequence` - creates a sequence of `0, ..., N - 1` with type `T`. +* `std::make_integer_sequence` - creates a sequence of `0, ..., N - 1` with type `T`. * `std::index_sequence_for` - converts a template parameter pack into an integer sequence. Convert an array into a tuple: @@ -179,18 +191,18 @@ decltype(auto) a2t(const std::array& a) { * Prevents code repetition when specifying the underlying type the pointer shall hold. * Most importantly, it provides exception-safety. Suppose we were calling a function `foo` like so: ```c++ -foo(std::unique_ptr{ new T{} }, function_that_throws(), std::unique_ptr{ new T{} }); +foo(std::unique_ptr{new T{}}, function_that_throws(), std::unique_ptr{new T{}}); ``` The compiler is free to call `new T{}`, then `function_that_throws()`, and so on... Since we have allocated data on the heap in the first construction of a `T`, we have introduced a leak here. With `std::make_unique`, we are given exception-safety: ```c++ foo(std::make_unique(), function_that_throws(), std::make_unique()); ``` -See the C++11 section on smart pointers for more information on `std::unique_ptr` and `std::shared_ptr`. +See the section on [smart pointers (C++11)](README.md#smart-pointers) for more information on `std::unique_ptr` and `std::shared_ptr`. ## 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. +* [C++ Rvalue References Explained](http://web.archive.org/web/20240324121501/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! diff --git a/CPP17.md b/CPP17.md index 644733c..56b966f 100644 --- a/CPP17.md +++ b/CPP17.md @@ -1,7 +1,7 @@ # C++17 ## Overview -Many of these descriptions and examples come from various resources (see [Acknowledgements](#acknowledgements) section), summarized in my own words. +Many of these descriptions and examples are taken from various resources (see [Acknowledgements](#acknowledgements) section) and 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) @@ -17,6 +17,9 @@ C++17 includes the following new language features: - [constexpr if](#constexpr-if) - [utf-8 character literals](#utf-8-character-literals) - [direct-list-initialization of enums](#direct-list-initialization-of-enums) +- [\[\[fallthrough\]\], \[\[nodiscard\]\], \[\[maybe_unused\]\] attributes](#fallthrough-nodiscard-maybe_unused-attributes) +- [\_\_has\_include](#\_\_has\_include) +- [class template argument deduction](#class-template-argument-deduction) C++17 includes the following new library features: - [std::variant](#stdvariant) @@ -25,7 +28,18 @@ C++17 includes the following new library features: - [std::string_view](#stdstring_view) - [std::invoke](#stdinvoke) - [std::apply](#stdapply) +- [std::filesystem](#stdfilesystem) +- [std::byte](#stdbyte) - [splicing for maps and sets](#splicing-for-maps-and-sets) +- [parallel algorithms](#parallel-algorithms) +- [std::sample](#stdsample) +- [std::clamp](#stdclamp) +- [std::reduce](#stdreduce) +- [prefix sum algorithms](#prefix-sum-algorithms) +- [gcd and lcm](#gcd-and-lcm) +- [std::not_fn](#stdnot_fn) +- [string conversion to/from numbers](#string-conversion-tofrom-numbers) +- [rounding functions for chrono durations and timepoints](#rounding-functions-for-chrono-durations-and-timepoints) ## C++17 Language Features @@ -35,18 +49,18 @@ Automatic template argument deduction much like how it's done for functions, but template struct MyContainer { T val; - MyContainer() : val() {} - MyContainer(T val) : val(val) {} + MyContainer() : val{} {} + MyContainer(T val) : val{val} {} // ... }; -MyContainer c1{ 1 }; // OK MyContainer +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 +template struct my_integer_sequence { // Implementation here ... }; @@ -61,9 +75,9 @@ auto seq2 = my_integer_sequence<0, 1, 2>(); ### 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. +* An expression of the form `(e1 op ... op e2)`, where `op` are fold-operators, is called a _binary fold_. Either `e1` or `e2` is an unexpanded parameter pack, but not both. ```c++ -template +template bool logicalAnd(Args... args) { // Binary folding. return (true && ... && args); @@ -73,7 +87,7 @@ bool& b2 = b; logicalAnd(b, b2, true); // == true ``` ```c++ -template +template auto sum(Args... args) { // Unary folding. return (... + args); @@ -82,22 +96,22 @@ 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`. +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 +auto x1 {1, 2, 3}; // error: not a single element +auto x2 = {1, 2, 3}; // x2 is std::initializer_list +auto x3 {3}; // x3 is int +auto x4 {3.0}; // x4 is double ``` ### constexpr lambda Compile-time lambdas using `constexpr`. ```c++ -auto identity = [] (int n) constexpr { return n; }; +auto identity = [](int n) constexpr { return n; }; static_assert(identity(123) == 123); ``` ```c++ -constexpr auto add = [] (int x, int y) { +constexpr auto add = [](int x, int y) { auto L = [=] { return x; }; auto R = [=] { return y; }; return [=] { return L() + R(); }; @@ -117,7 +131,7 @@ static_assert(addOne(1) == 2); 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 }; + int value {123}; auto getValueCopy() { return [*this] { return value; }; } @@ -146,6 +160,16 @@ S x2 = S{123}; // mov eax, dword ptr [.L_ZZ4mainE2x2] // .L_ZZ4mainE2x2: .long 123 ``` +It can also be used to declare and define a static member variable, such that it does not need to be initialized in the source file. +```c++ +struct S { + S() : id{count++} {} + ~S() { count--; } + int id; + static inline int count{0}; // declare and initialize count to 0 within the class +}; +``` + ### Nested namespaces Using the namespace resolution operator to create nested namespace definitions. ```c++ @@ -156,14 +180,17 @@ namespace A { } } } -// vs. +``` + +The code above can be written like this: +```c++ 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. +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`](README.md#tuples), `std::pair`, [`std::array`](README.md#stdarray), and aggregate structures. ```c++ using Coordinate = std::pair; Coordinate origin() { @@ -174,6 +201,18 @@ const auto [ x, y ] = origin(); x; // == 0 y; // == 0 ``` +```c++ +std::unordered_map mapping { + {"a", 1}, + {"b", 2}, + {"c", 3} +}; + +// Destructure by reference. +for (const auto& [key, value] : mapping) { + // Do something with key and value +} +``` ### Selection statements with initializer New versions of the `if` and `switch` statements which simplify common code patterns and help users keep scopes tight. @@ -218,22 +257,144 @@ struct S {}; static_assert(isIntegral() == false); ``` -### UTF-8 Character Literals +### 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 +### 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 b {0}; // OK +byte c {-1}; // ERROR byte d = byte{1}; // OK byte e = byte{256}; // ERROR ``` +### \[\[fallthrough\]\], \[\[nodiscard\]\], \[\[maybe_unused\]\] attributes +C++17 introduces three new attributes: `[[fallthrough]]`, `[[nodiscard]]` and `[[maybe_unused]]`. +* `[[fallthrough]]` indicates to the compiler that falling through in a switch statement is intended behavior. This attribute may only be used in a switch statement, and must be placed before the next case/default label. +```c++ +switch (n) { + case 1: + // ... + [[fallthrough]]; + case 2: + // ... + break; + case 3: + // ... + [[fallthrough]]; + default: + // ... +} +``` + +* `[[nodiscard]]` issues a warning when either a function or class has this attribute and its return value is discarded. +```c++ +[[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' +``` +```c++ +// 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]]` indicates to the compiler that a variable or parameter might be unused and is intended. +```c++ +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 (operand)` operator may be used in `#if` and `#elif` expressions to check whether a header or source file (`operand`) is available for inclusion or not. + +One use case of this would be using two libraries that work the same way, using the backup/experimental one if the preferred one is not found on the system. + +```c++ +#ifdef __has_include +# if __has_include() +# include +# define have_optional 1 +# elif __has_include() +# include +# define have_optional 1 +# define experimental_optional +# else +# define have_optional 0 +# endif +#endif +``` + +It can also be used to include headers existing under different names or locations on various platforms, without knowing which platform the program is running on, OpenGL headers are a good example for this which are located in `OpenGL\` directory on macOS and `GL\` on other platforms. + +```c++ +#ifdef __has_include +# if __has_include() +# include +# include +# elif __has_include() +# include +# include +# else +# error No suitable OpenGL headers found. +# endif +#endif +``` + +### Class template argument deduction +*Class template argument deduction* (CTAD) allows the compiler to deduce template arguments from constructor arguments. +```c++ +std::vector v{ 1, 2, 3 }; // deduces std::vector + +std::mutex mtx; +auto lck = std::lock_guard{ mtx }; // deduces to std::lock_guard + +auto p = new std::pair{ 1.0, 2.0 }; // deduces to std::pair* +``` + +For user-defined types, *deduction guides* can be used to guide the compiler how to deduce template arguments if applicable: +```c++ +template +struct container { + container(T t) {} + + template + container(Iter beg, Iter end); +}; + +// deduction guide +template +container(Iter b, Iter e) -> container::value_type>; + +container a{ 7 }; // OK: deduces container + +std::vector v{ 1.0, 2.0, 3.0 }; +auto b = container{ v.begin(), v.end() }; // OK: deduces container + +container c{ 5, 6 }; // ERROR: std::iterator_traits::value_type is not a type +``` + ## C++17 Library Features ### std::variant @@ -269,7 +430,7 @@ if (auto str = create(true)) { ### std::any A type-safe container for single values of any type. ```c++ -std::any x{ 5 }; +std::any x {5}; x.has_value() // == true std::any_cast(x) // == 5 std::any_cast(x) = 10; @@ -280,58 +441,86 @@ std::any_cast(x) // == 10 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" }; +std::string_view cppstr {"foo"}; // Wide strings. -std::wstring_view wcstr_v{ L"baz" }; +std::wstring_view wcstr_v {L"baz"}; // Character arrays. char array[3] = {'b', 'a', 'r'}; -std::string_view array_v(array, sizeof array); +std::string_view array_v(array, std::size(array)); ``` ```c++ -std::string str{ " trim me" }; -std::string_view v{ str }; +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. +Invoke a `Callable` object with parameters. Examples of *callable* objects are `std::function` or lambdas; objects that can be called similarly to a regular function. ```c++ template class Proxy { - Callable c; + 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(Callable c) : c_{ std::move(c) } {} + + template + decltype(auto) operator()(Args&&... args) { + // ... + return std::invoke(c_, std::forward(args)...); + } }; -Proxy p{ add }; + +const 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) { +auto add = [](int x, int y) { return x + y; }; -std::apply(add, std::make_tuple( 1, 2 )); // == 3 +std::apply(add, std::make_tuple(1, 2)); // == 3 +``` + +### 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")); + } +} ``` +### std::byte +The new `std::byte` type provides a standard way of representing data as a byte. Benefits of using `std::byte` over `char` or `unsigned char` is that it is not a character type, and is also not an arithmetic type; while the only operator overloads available are bitwise operations. +```c++ +std::byte a {0}; +std::byte b {0xFF}; +int i = std::to_integer(b); // 0xFF +std::byte c = a & b; +int j = std::to_integer(c); // 0 +``` +Note that `std::byte` is simply an enum, and braced initialization of enums become possible thanks to [direct-list-initialization of enums](#direct-list-initialization-of-enums). + ### 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" } }; +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" } }; @@ -339,8 +528,8 @@ dst.insert(src.extract(2)); // Cheap remove and insert of { 2, "two" } from `src Inserting an entire set: ```c++ -std::set src{1, 3, 5}; -std::set dst{2, 4, 5}; +std::set src {1, 3, 5}; +std::set dst {2, 4, 5}; dst.merge(src); // src == { 5 } // dst == { 1, 2, 3, 4, 5 } @@ -358,16 +547,160 @@ s2.insert(elementFactory()); Changing the key of a map element: ```c++ -std::map m{ { 1, "one" }, { 2, "two" }, { 3, "three" } }; +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" } } ``` +### Parallel algorithms +Many of the STL algorithms, such as the `copy`, `find` and `sort` methods, started to support the *parallel execution policies*: `seq`, `par` and `par_unseq` which translate to "sequentially", "parallel" and "parallel unsequenced". + +```c++ +std::vector 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)); +``` + +### std::sample +Samples n elements in the given sequence (without replacement) where every element has an equal chance of being selected. +```c++ +const std::string ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +std::string guid; +// Sample 5 characters from ALLOWED_CHARS. +std::sample(ALLOWED_CHARS.begin(), ALLOWED_CHARS.end(), std::back_inserter(guid), + 5, std::mt19937{ std::random_device{}() }); + +std::cout << guid; // e.g. G1fW2 +``` + +### std::clamp +Clamp given value between a lower and upper bound. +```c++ +std::clamp(42, -1, 1); // == 1 +std::clamp(-42, -1, 1); // == -1 +std::clamp(0, -1, 1); // == 0 + +// `std::clamp` also accepts a custom comparator: +std::clamp(0, -1, 1, std::less<>{}); // == 0 +``` + +### std::reduce +Fold over a given range of elements. Conceptually similar to `std::accumulate`, but `std::reduce` will perform the fold in parallel. Due to the fold being done in parallel, if you specify a binary operation, it is required to be associative and commutative. A given binary operation also should not change any element or invalidate any iterators within the given range. + +The default binary operation is std::plus with an initial value of 0. +```c++ +const std::array a{ 1, 2, 3 }; +std::reduce(std::cbegin(a), std::cend(a)); // == 6 +// Using a custom binary op: +std::reduce(std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6 +``` +Additionally you can specify transformations for reducers: +```c++ +std::transform_reduce(std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten); // == 60 + +const std::array b{ 1, 2, 3 }; +const auto product_times_ten = [](const auto a, const auto b) { return a * b * 10; }; + +std::transform_reduce(std::cbegin(a), std::cend(a), std::cbegin(b), 0, std::plus<>{}, product_times_ten); // == 140 +``` + +### Prefix sum algorithms +Support for prefix sums (both inclusive and exclusive scans) along with transformations. +```c++ +const std::array a{ 1, 2, 3 }; + +std::inclusive_scan(std::cbegin(a), std::cend(a), + std::ostream_iterator{ std::cout, " " }, std::plus<>{}); // 1 3 6 + +std::exclusive_scan(std::cbegin(a), std::cend(a), + std::ostream_iterator{ std::cout, " " }, 0, std::plus<>{}); // 0 1 3 + +const auto times_ten = [](const auto n) { return n * 10; }; + +std::transform_inclusive_scan(std::cbegin(a), std::cend(a), + std::ostream_iterator{ std::cout, " " }, std::plus<>{}, times_ten); // 10 30 60 + +std::transform_exclusive_scan(std::cbegin(a), std::cend(a), + std::ostream_iterator{ std::cout, " " }, 0, std::plus<>{}, times_ten); // 0 10 30 +``` + +### GCD and LCM +Greatest common divisor (GCD) and least common multiple (LCM). +```c++ +const int p = 9; +const int q = 3; +std::gcd(p, q); // == 3 +std::lcm(p, q); // == 9 +``` + +### std::not_fn +Utility function that returns the negation of the result of the given function. +```c++ +const std::ostream_iterator ostream_it{ std::cout, " " }; +const auto is_even = [](const auto n) { return n % 2 == 0; }; +std::vector v{ 0, 1, 2, 3, 4 }; + +// Print all even numbers. +std::copy_if(std::cbegin(v), std::cend(v), ostream_it, is_even); // 0 2 4 +// Print all odd (not even) numbers. +std::copy_if(std::cbegin(v), std::cend(v), ostream_it, std::not_fn(is_even)); // 1 3 +``` + +### String conversion to/from numbers +Convert integrals and floats to a string or vice-versa. Conversions are non-throwing, do not allocate, and are more secure than the equivalents from the C standard library. + +Users are responsible for allocating enough storage required for `std::to_chars`, or the function will fail by setting the error code object in its return value. + +These functions allow you to optionally pass a base (defaults to base-10) or a format specifier for floating type input. + +* `std::to_chars` returns a (non-const) char pointer which is one-past-the-end of the string that the function wrote to inside the given buffer, and an error code object. +* `std::from_chars` returns a const char pointer which on success is equal to the end pointer passed to the function, and an error code object. + +Both error code objects returned from these functions are equal to the default-initialized error code object on success. + +Convert the number `123` to a `std::string`: +```c++ +const int n = 123; + +// Can use any container, string, array, etc. +std::string str; +str.resize(3); // hold enough storage for each digit of `n` + +const auto [ ptr, ec ] = std::to_chars(str.data(), str.data() + str.size(), n); + +if (ec == std::errc{}) { std::cout << str << std::endl; } // 123 +else { /* handle failure */ } +``` + +Convert from a `std::string` with value `"123"` to an integer: +```c++ +const std::string str{ "123" }; +int n; + +const auto [ ptr, ec ] = std::from_chars(str.data(), str.data() + str.size(), n); + +if (ec == std::errc{}) { std::cout << n << std::endl; } // 123 +else { /* handle failure */ } +``` + +### Rounding functions for chrono durations and timepoints +Provides abs, round, ceil, and floor helper functions for `std::chrono::duration` and `std::chrono::time_point`. +```c++ +using seconds = std::chrono::seconds; +std::chrono::milliseconds d{ 5500 }; +std::chrono::abs(d); // == 5s +std::chrono::round(d); // == 6s +std::chrono::ceil(d); // == 6s +std::chrono::floor(d); // == 5s +``` + ## 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. +* [C++ Rvalue References Explained](http://web.archive.org/web/20240324121501/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! diff --git a/CPP20.md b/CPP20.md new file mode 100644 index 0000000..08dfabf --- /dev/null +++ b/CPP20.md @@ -0,0 +1,763 @@ +# C++20 + +## Overview +Many of these descriptions and examples are taken from various resources (see [Acknowledgements](#acknowledgements) section) and summarized in my own words. + +C++20 includes the following new language features: +- [coroutines](#coroutines) +- [concepts](#concepts) +- [three-way comparison](#three-way-comparison) +- [designated initializers](#designated-initializers) +- [template syntax for lambdas](#template-syntax-for-lambdas) +- [range-based for loop with initializer](#range-based-for-loop-with-initializer) +- [\[\[likely\]\] and \[\[unlikely\]\] attributes](#likely-and-unlikely-attributes) +- [deprecate implicit capture of this](#deprecate-implicit-capture-of-this) +- [class types in non-type template parameters](#class-types-in-non-type-template-parameters) +- [constexpr virtual functions](#constexpr-virtual-functions) +- [explicit(bool)](#explicitbool) +- [immediate functions](#immediate-functions) +- [using enum](#using-enum) +- [lambda capture of parameter pack](#lambda-capture-of-parameter-pack) +- [char8_t](#char8_t) +- [constinit](#constinit) +- [\_\_VA\_OPT\_\_](#__VA_OPT__) + +C++20 includes the following new library features: +- [concepts library](#concepts-library) +- [formatting library](#formatting-library) +- [synchronized buffered outputstream](#synchronized-buffered-outputstream) +- [std::span](#stdspan) +- [bit operations](#bit-operations) +- [math constants](#math-constants) +- [std::is_constant_evaluated](#stdis_constant_evaluated) +- [std::make_shared supports arrays](#stdmake_shared-supports-arrays) +- [starts_with and ends_with on strings](#starts_with-and-ends_with-on-strings) +- [check if associative container has element](#check-if-associative-container-has-element) +- [std::bit_cast](#stdbit_cast) +- [std::midpoint](#stdmidpoint) +- [std::to_array](#stdto_array) +- [std::bind_front](#stdbind_front) +- [uniform container erasure](#uniform-container-erasure) +- [three-way comparison helpers](#three-way-comparison-helpers) +- [std::lexicographical_compare_three_way](#stdlexicographical_compare_three_way) + +## C++20 Language Features + +### Coroutines + +> **Note:** While these examples illustrate how to use coroutines at a basic level, there is lots more going on when the code is compiled. These examples are not meant to be complete coverage of C++20's coroutines. Since the `generator` and `task` classes are not provided by the standard library yet, I used the cppcoro library to compile these examples. + +_Coroutines_ are special functions that can have their execution suspended and resumed. To define a coroutine, the `co_return`, `co_await`, or `co_yield` keywords must be present in the function's body. C++20's coroutines are stackless; unless optimized out by the compiler, their state is allocated on the heap. + +An example of a coroutine is a _generator_ function, which yields (i.e. generates) a value at each invocation: +```c++ +generator range(int start, int end) { + while (start < end) { + co_yield start; + start++; + } + + // Implicit co_return at the end of this function: + // co_return; +} + +for (int n : range(0, 10)) { + std::cout << n << std::endl; +} +``` +The above `range` generator function generates values starting at `start` until `end` (exclusive), with each iteration step yielding the current value stored in `start`. The generator maintains its state across each invocation of `range` (in this case, the invocation is for each iteration in the for loop). `co_yield` takes the given expression, yields (i.e. returns) its value, and suspends the coroutine at that point. Upon resuming, execution continues after the `co_yield`. + +Another example of a coroutine is a _task_, which is an asynchronous computation that is executed when the task is awaited: +```c++ +task echo(socket s) { + for (;;) { + auto data = co_await s.async_read(); + co_await async_write(s, data); + } + + // Implicit co_return at the end of this function: + // co_return; +} +``` +In this example, the `co_await` keyword is introduced. This keyword takes an expression and suspends execution if the thing you're awaiting on (in this case, the read or write) is not ready, otherwise you continue execution. (Note that under the hood, `co_yield` uses `co_await`.) + +Using a task to lazily evaluate a value: +```c++ +task calculate_meaning_of_life() { + co_return 42; +} + +auto meaning_of_life = calculate_meaning_of_life(); +// ... +co_await meaning_of_life; // == 42 +``` + +### Concepts +_Concepts_ are named compile-time predicates which constrain types. They take the following form: +``` +template < template-parameter-list > +concept concept-name = constraint-expression; +``` +where `constraint-expression` evaluates to a constexpr Boolean. _Constraints_ should model semantic requirements, such as whether a type is a numeric or hashable. A compiler error results if a given type does not satisfy the concept it's bound by (i.e. `constraint-expression` returns `false`). Because constraints are evaluated at compile-time, they can provide more meaningful error messages and runtime safety. +```c++ +// `T` is not limited by any constraints. +template +concept always_satisfied = true; +// Limit `T` to integrals. +template +concept integral = std::is_integral_v; +// Limit `T` to both the `integral` constraint and signedness. +template +concept signed_integral = integral && std::is_signed_v; +// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint. +template +concept unsigned_integral = integral && !signed_integral; +``` +There are a variety of syntactic forms for enforcing concepts: +```c++ +// Forms for function parameters: +// `T` is a constrained type template parameter. +template +void f(T v); + +// `T` is a constrained type template parameter. +template + requires my_concept +void f(T v); + +// `T` is a constrained type template parameter. +template +void f(T v) requires my_concept; + +// `v` is a constrained deduced parameter. +void f(my_concept auto v); + +// `v` is a constrained non-type template parameter. +template +void g(); + +// Forms for auto-deduced variables: +// `foo` is a constrained auto-deduced value. +my_concept auto foo = ...; + +// Forms for lambdas: +// `T` is a constrained type template parameter. +auto f = [] (T v) { + // ... +}; +// `T` is a constrained type template parameter. +auto f = [] requires my_concept (T v) { + // ... +}; +// `T` is a constrained type template parameter. +auto f = [] (T v) requires my_concept { + // ... +}; +// `v` is a constrained deduced parameter. +auto f = [](my_concept auto v) { + // ... +}; +// `v` is a constrained non-type template parameter. +auto g = [] () { + // ... +}; +``` +The `requires` keyword is used either to start a `requires` clause or a `requires` expression: +```c++ +template + requires my_concept // `requires` clause. +void f(T); + +template +concept callable = requires (T f) { f(); }; // `requires` expression. + +template + requires requires (T x) { x + x; } // `requires` clause and expression on same line. +T add(T a, T b) { + return a + b; +} +``` +Note that the parameter list in a `requires` expression is optional. Each requirement in a `requires` expression are one of the following: + +* **Simple requirements** - asserts that the given expression is valid. + +```c++ +template +concept callable = requires (T f) { f(); }; +``` +* **Type requirements** - denoted by the `typename` keyword followed by a type name, asserts that the given type name is valid. + +```c++ +struct foo { + int foo; +}; + +struct bar { + using value = int; + value data; +}; + +struct baz { + using value = int; + value data; +}; + +// Using SFINAE, enable if `T` is a `baz`. +template >> +struct S {}; + +template +using Ref = T&; + +template +concept C = requires { + // Requirements on type `T`: + typename T::value; // A) has an inner member named `value` + typename S; // B) must have a valid class template specialization for `S` + typename Ref; // C) must be a valid alias template substitution +}; + +template +void g(T a); + +g(foo{}); // ERROR: Fails requirement A. +g(bar{}); // ERROR: Fails requirement B. +g(baz{}); // PASS. +``` +* **Compound requirements** - an expression in braces followed by a trailing return type or type constraint. + +```c++ +template +concept C = requires(T x) { + {*x} -> std::convertible_to; // the type of the expression `*x` is convertible to `T::inner` + {x + 1} -> std::same_as; // the expression `x + 1` satisfies `std::same_as` + {x * 1} -> std::convertible_to; // the type of the expression `x * 1` is convertible to `T` +}; +``` +* **Nested requirements** - denoted by the `requires` keyword, specify additional constraints (such as those on local parameter arguments). + +```c++ +template +concept C = requires(T x) { + requires std::same_as; +}; +``` +See also: [concepts library](#concepts-library). + +### Three-way comparison +C++20 introduces the spaceship operator (`<=>`) as a new way to write comparison functions that reduce boilerplate and help developers define clearer comparison semantics. Defining a three-way comparison operator will autogenerate the other comparison operator functions (i.e. `==`, `!=`, `<`, etc.). + +Three orderings are introduced: +* `std::strong_ordering`: The strong ordering distinguishes between items being equal (identical and interchangeable). Provides `less`, `greater`, `equivalent`, and `equal` ordering. Examples of comparisons: searching for a specific value in a list, values of integers, case-sensitive strings. +* `std::weak_ordering`: The weak ordering distinguishes between items being equivalent (not identical, but can be interchangeable for the purposes of comparison). Provides `less`, `greater`, and `equivalent` ordering. Examples of comparisons: case-insensitive strings, sorting, comparing some but not all visible members of a class. +* `std::partial_ordering`: The partial ordering follows the same principle of weak ordering but includes the case when an ordering isn't possible. Provides `less`, `greater`, `equivalent`, and `unordered` ordering. Examples of comparisons: floating-point values (e.g. `NaN`). + +A defaulted three-way comparison operator does a member-wise comparison: +```c++ +struct foo { + int a; + bool b; + char c; + + // Compare `a` first, then `b`, then `c` ... + friend auto operator<=>(const foo&) const = default; +}; + +foo f1{0, false, 'a'}, f2{0, true, 'b'}; +f1 < f2; // == true +f1 == f2; // == false +f1 >= f2; // == false +``` + +You can also define your own comparisons: +```c++ +struct foo { + int x; + bool b; + char c; + + friend std::strong_ordering operator<=>(const foo& other) const { + return x <=> other.x; + } +}; + +foo f1{0, false, 'a'}, f2{0, true, 'b'}; +f1 < f2; // == false +f1 == f2; // == true +f1 >= f2; // == true +``` + +### Designated initializers +C-style designated initializer syntax. Any member fields that are not explicitly listed in the designated initializer list are default-initialized. +```c++ +struct A { + int x; + int y; + int z = 123; +}; + +A a {.x = 1, .z = 2}; // a.x == 1, a.y == 0, a.z == 2 +``` + +### Template syntax for lambdas +Use familiar template syntax in lambda expressions. +```c++ +auto f = [](std::vector v) { + // ... +}; +``` + +### Range-based for loop with initializer +This feature simplifies common code patterns, helps keep scopes tight, and offers an elegant solution to a common lifetime problem. +```c++ +for (auto v = std::vector{1, 2, 3}; auto& e : v) { + std::cout << e; +} +// prints "123" +``` + +### \[\[likely\]\] and \[\[unlikely\]\] attributes +Provides a hint to the optimizer that the labelled statement has a high probability of being executed. +```c++ +switch (n) { +case 1: + // ... + break; + +[[likely]] case 2: // n == 2 is considered to be arbitrarily more + // ... // likely than any other value of n + break; +} +``` + +If one of the likely/unlikely attributes appears after the right parenthesis of an if-statement, +it indicates that the branch is likely/unlikely to have its substatement (body) executed. +```c++ +int random = get_random_number_between_x_and_y(0, 3); +if (random > 0) [[likely]] { + // body of if statement + // ... +} +``` + +It can also be applied to the substatement (body) of an iteration statement. +```c++ +while (unlikely_truthy_condition) [[unlikely]] { + // body of while statement + // ... +} +``` + +### Deprecate implicit capture of this +Implicitly capturing `this` in a lambda capture using `[=]` is now deprecated; prefer capturing explicitly using `[=, this]` or `[=, *this]`. +```c++ +struct int_value { + int n = 0; + auto getter_fn() { + // BAD: + // return [=]() { return n; }; + + // GOOD: + return [=, *this]() { return n; }; + } +}; +``` + +### Class types in non-type template parameters +Classes can now be used in non-type template parameters. Objects passed in as template arguments have the type `const T`, where `T` is the type of the object, and has static storage duration. +```c++ +struct foo { + foo() = default; + constexpr foo(int) {} +}; + +template +auto get_foo() { + return f; +} + +get_foo(); // uses implicit constructor +get_foo(); +``` + +### constexpr virtual functions +Virtual functions can now be `constexpr` and evaluated at compile-time. `constexpr` virtual functions can override non-`constexpr` virtual functions and vice-versa. +```c++ +struct X1 { + virtual int f() const = 0; +}; + +struct X2: public X1 { + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 { + virtual int f() const { return 3; } +}; + +struct X4: public X3 { + constexpr virtual int f() const { return 4; } +}; + +constexpr X4 x4; +x4.f(); // == 4 +``` + +### explicit(bool) +Conditionally select at compile-time whether a constructor is made explicit or not. `explicit(true)` is the same as specifying `explicit`. +```c++ +struct foo { + // Specify non-integral types (strings, floats, etc.) require explicit construction. + template + explicit(!std::is_integral_v) foo(T) {} +}; + +foo a = 123; // OK +foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true) +foo c {"123"}; // OK +``` + +### Immediate functions +Similar to `constexpr` functions, but functions with a `consteval` specifier must produce a constant. These are called `immediate functions`. +```c++ +consteval int sqr(int n) { + return n * n; +} + +constexpr int r = sqr(100); // OK +int x = 100; +int r2 = sqr(x); // ERROR: the value of 'x' is not usable in a constant expression + // OK if `sqr` were a `constexpr` function +``` + +### using enum +Bring an enum's members into scope to improve readability. Before: +```c++ +enum class rgba_color_channel { red, green, blue, alpha }; + +std::string_view to_string(rgba_color_channel channel) { + switch (channel) { + case rgba_color_channel::red: return "red"; + case rgba_color_channel::green: return "green"; + case rgba_color_channel::blue: return "blue"; + case rgba_color_channel::alpha: return "alpha"; + } +} +``` +After: +```c++ +enum class rgba_color_channel { red, green, blue, alpha }; + +std::string_view to_string(rgba_color_channel my_channel) { + switch (my_channel) { + using enum rgba_color_channel; + case red: return "red"; + case green: return "green"; + case blue: return "blue"; + case alpha: return "alpha"; + } +} +``` + +### Lambda capture of parameter pack +Capture parameter packs by value: +```c++ +template +auto f(Args&&... args){ + // BY VALUE: + return [...args = std::forward(args)] { + // ... + }; +} +``` +Capture parameter packs by reference: +```c++ +template +auto f(Args&&... args){ + // BY REFERENCE: + return [&...args = std::forward(args)] { + // ... + }; +} +``` + +### char8_t +Provides a standard type for representing UTF-8 strings. +```c++ +char8_t utf8_str[] = u8"\u0123"; +``` + +### constinit +The `constinit` specifier requires that a variable must be initialized at compile-time. +```c++ +const char* g() { return "dynamic initialization"; } +constexpr const char* f(bool p) { return p ? "constant initializer" : g(); } + +constinit const char* c = f(true); // OK +constinit const char* d = g(false); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time. +``` + +### \_\_VA\_OPT\_\_ +Helps support variadic macros by evaluating to the given argument if the variadic macro is non-empty. +```c++ +#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__) +F(a, b, c) // replaced by f(0, a, b, c) +F() // replaced by f(0) +``` + +## C++20 Library Features + +### Concepts library +Concepts are also provided by the standard library for building more complicated concepts. Some of these include: + +**Core language concepts:** +- `same_as` - specifies two types are the same. +- `derived_from` - specifies that a type is derived from another type. +- `convertible_to` - specifies that a type is implicitly convertible to another type. +- `common_with` - specifies that two types share a common type. +- `integral` - specifies that a type is an integral type. +- `default_constructible` - specifies that an object of a type can be default-constructed. + + **Comparison concepts:** +- `boolean` - specifies that a type can be used in Boolean contexts. +- `equality_comparable` - specifies that `operator==` is an equivalence relation. + + **Object concepts:** +- `movable` - specifies that an object of a type can be moved and swapped. +- `copyable` - specifies that an object of a type can be copied, moved, and swapped. +- `semiregular` - specifies that an object of a type can be copied, moved, swapped, and default constructed. +- `regular` - specifies that a type is _regular_, that is, it is both `semiregular` and `equality_comparable`. + + **Callable concepts:** +- `invocable` - specifies that a callable type can be invoked with a given set of argument types. +- `predicate` - specifies that a callable type is a Boolean predicate. + +See also: [concepts](#concepts). + +### Formatting library +Combine the simplicity of `printf` with the type-safety of `iostream`. Uses braces as placeholders, and supports custom formatting similar to printf-style specifiers. +```c++ +std::format("{1} {0}", "world", "hello"); // == "hello world" + +int x = 123; +std::string str = std::format("x: {}", x); // str == "x: 123" + +// Format to an output iterator: +for (auto x : {1, 2, 3}) { + std::format_to(std::ostream_iterator{std::cout, "\n"}, "{}", x); +} +``` + +To format custom types: +```c++ +struct fraction { + int numerator; + int denominator; +}; + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + auto format(const fraction& f, std::format_context& ctx) const { + return std::format_to(ctx.out(), "{0:d}/{1:d}", f.numerator, f.denominator); + } +}; + +fraction f{1, 2}; +std::format("{}", f); // == "1/2" +``` + +### Synchronized buffered outputstream +Buffers output operations for the wrapped output stream ensuring synchronization (i.e. no interleaving of output). +```c++ +std::osyncstream{std::cout} << "The value of x is:" << x << std::endl; +``` + +### std::span +A span is a view (i.e. non-owning) of a container providing bounds-checked access to a contiguous group of elements. Since views do not own their elements they are cheap to construct and copy -- a simplified way to think about views is they are holding references to their data. As opposed to maintaining a pointer/iterator and length field, a span wraps both of those up in a single object. + +Spans can be dynamically-sized or fixed-sized (known as their *extent*). Fixed-sized spans benefit from bounds-checking. + +Span doesn't propogate const so to construct a read-only span use `std::span`. + +Example: using a dynamically-sized span to print integers from various containers. +```c++ +void print_ints(std::span ints) { + for (const auto n : ints) { + std::cout << n << std::endl; + } +} + +print_ints(std::vector{ 1, 2, 3 }); +print_ints(std::array{ 1, 2, 3, 4, 5 }); + +int a[10] = { 0 }; +print_ints(a); +// etc. +``` + +Example: a statically-sized span will fail to compile for containers that don't match the extent of the span. +```c++ +void print_three_ints(std::span ints) { + for (const auto n : ints) { + std::cout << n << std::endl; + } +} + +print_three_ints(std::vector{ 1, 2, 3 }); // ERROR +print_three_ints(std::array{ 1, 2, 3, 4, 5 }); // ERROR +int a[10] = { 0 }; +print_three_ints(a); // ERROR + +std::array b = { 1, 2, 3 }; +print_three_ints(b); // OK + +// You can construct a span manually if required: +std::vector c{ 1, 2, 3 }; +print_three_ints(std::span{ c.data(), 3 }); // OK: set pointer and length field. +print_three_ints(std::span{ c.cbegin(), c.cend() }); // OK: use iterator pairs. +``` + +### Bit operations +C++20 provides a new `` header which provides some bit operations including popcount. +```c++ +std::popcount(0u); // 0 +std::popcount(1u); // 1 +std::popcount(0b1111'0000u); // 4 +``` + +### Math constants +Mathematical constants including PI, Euler's number, etc. defined in the `` header. +```c++ +std::numbers::pi; // 3.14159... +std::numbers::e; // 2.71828... +``` + +### std::is_constant_evaluated +Predicate function which is truthy when it is called in a compile-time context. +```c++ +constexpr bool is_compile_time() { + return std::is_constant_evaluated(); +} + +constexpr bool a = is_compile_time(); // true +bool b = is_compile_time(); // false +``` + +### std::make_shared supports arrays +```c++ +auto p = std::make_shared(5); // pointer to `int[5]` +// OR +auto p = std::make_shared(); // pointer to `int[5]` +``` + +### starts_with and ends_with on strings +Strings (and string views) now have the `starts_with` and `ends_with` member functions to check if a string starts or ends with the given string. +```c++ +std::string str = "foobar"; +str.starts_with("foo"); // true +str.ends_with("baz"); // false +``` + +### Check if associative container has element +Associative containers such as sets and maps have a `contains` member function, which can be used instead of the "find and check end of iterator" idiom. +```c++ +std::map map {{1, 'a'}, {2, 'b'}}; +map.contains(2); // true +map.contains(123); // false + +std::set set {1, 2, 3}; +set.contains(2); // true +``` + +### std::bit_cast +A safer way to reinterpret an object from one type to another. +```c++ +float f = 123.0; +int i = std::bit_cast(f); +``` + +### std::midpoint +Calculate the midpoint of two integers safely (without overflow). +```c++ +std::midpoint(1, 3); // == 2 +``` + +### std::to_array +Converts the given array/"array-like" object to a `std::array`. +```c++ +std::to_array("foo"); // returns `std::array` +std::to_array({1, 2, 3}); // returns `std::array` + +int a[] = {1, 2, 3}; +std::to_array(a); // returns `std::array` +``` + +### std::bind_front +Binds the first N arguments (where N is the number of arguments after the given function to `std::bind_front`) to a given free function, lambda, or member function. +```c++ +const auto f = [](int a, int b, int c) { return a + b + c; }; +const auto g = std::bind_front(f, 1, 1); +g(1); // == 3 +``` + +### Uniform container erasure +Provides `std::erase` and/or `std::erase_if` for a variety of STL containers such as string, list, vector, map, etc. + +For erasing by value use `std::erase`, or to specify a predicate when to erase elements use `std::erase_if`. Both functions return the number of erased elements. + +```c++ +std::vector v{0, 1, 0, 2, 0, 3}; +std::erase(v, 0); // v == {1, 2, 3} +std::erase_if(v, [](int n) { return n == 0; }); // v == {1, 2, 3} +``` + +### Three-way comparison helpers +Helper functions for giving names to comparison results: +```c++ +std::is_eq(0 <=> 0); // == true +std::is_lteq(0 <=> 1); // == true +std::is_gt(0 <=> 1); // == false +``` + +See also: [three-way comparison](#three-way-comparison). + +### std::lexicographical_compare_three_way +Lexicographically compares two ranges using three-way comparison and produces a result of the strongest applicable comparison category type. +```c++ +std::vector a{0, 0, 0}, b{0, 0, 0}, c{1, 1, 1}; + +auto cmp_ab = std::lexicographical_compare_three_way( + a.begin(), a.end(), b.begin(), b.end()); +std::is_eq(cmp_ab); // == true + +auto cmp_ac = std::lexicographical_compare_three_way( + a.begin(), a.end(), c.begin(), c.end()); +std::is_lt(cmp_ac); // == true +``` + +See also: [three-way comparison](#three-way-comparison), [three-way comparison helpers](#three-way-comparison-helpers). + +## Acknowledgements +* [cppreference](http://en.cppreference.com/w/cpp) - especially useful for finding examples and documentation of new library features. +* [C++ Rvalue References Explained](http://web.archive.org/web/20240324121501/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 diff --git a/LICENSE b/LICENSE index 5483565..fa52f4a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Anthony Calandra +Copyright (c) 2024 Anthony Calandra Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index f7e5515..f4dfbae 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,44 @@ -# C++17/14/11 +# C++20/17/14/11 ## Overview -Many of these descriptions and examples come from various resources (see [Acknowledgements](#acknowledgements) section), summarized in my own words. -Also, there are now dedicated readme pages for each major C++ version. +C++20 includes the following new language features: +- [coroutines](#coroutines) +- [concepts](#concepts) +- [three-way comparison](#three-way-comparison) +- [designated initializers](#designated-initializers) +- [template syntax for lambdas](#template-syntax-for-lambdas) +- [range-based for loop with initializer](#range-based-for-loop-with-initializer) +- [\[\[likely\]\] and \[\[unlikely\]\] attributes](#likely-and-unlikely-attributes) +- [deprecate implicit capture of this](#deprecate-implicit-capture-of-this) +- [class types in non-type template parameters](#class-types-in-non-type-template-parameters) +- [constexpr virtual functions](#constexpr-virtual-functions) +- [explicit(bool)](#explicitbool) +- [immediate functions](#immediate-functions) +- [using enum](#using-enum) +- [lambda capture of parameter pack](#lambda-capture-of-parameter-pack) +- [char8_t](#char8_t) +- [constinit](#constinit) +- [\_\_VA\_OPT\_\_](#__VA_OPT__) + +C++20 includes the following new library features: +- [concepts library](#concepts-library) +- [formatting library](#formatting-library) +- [synchronized buffered outputstream](#synchronized-buffered-outputstream) +- [std::span](#stdspan) +- [bit operations](#bit-operations) +- [math constants](#math-constants) +- [std::is_constant_evaluated](#stdis_constant_evaluated) +- [std::make_shared supports arrays](#stdmake_shared-supports-arrays) +- [starts_with and ends_with on strings](#starts_with-and-ends_with-on-strings) +- [check if associative container has element](#check-if-associative-container-has-element) +- [std::bit_cast](#stdbit_cast) +- [std::midpoint](#stdmidpoint) +- [std::to_array](#stdto_array) +- [std::bind_front](#stdbind_front) +- [uniform container erasure](#uniform-container-erasure) +- [three-way comparison helpers](#three-way-comparison-helpers) +- [std::lexicographical_compare_three_way](#stdlexicographical_compare_three_way) C++17 includes the following new language features: - [template argument deduction for class templates](#template-argument-deduction-for-class-templates) @@ -19,6 +54,9 @@ C++17 includes the following new language features: - [constexpr if](#constexpr-if) - [utf-8 character literals](#utf-8-character-literals) - [direct-list-initialization of enums](#direct-list-initialization-of-enums) +- [\[\[fallthrough\]\], \[\[nodiscard\]\], \[\[maybe_unused\]\] attributes](#fallthrough-nodiscard-maybe_unused-attributes) +- [\_\_has\_include](#\_\_has\_include) +- [class template argument deduction](#class-template-argument-deduction) C++17 includes the following new library features: - [std::variant](#stdvariant) @@ -27,7 +65,18 @@ C++17 includes the following new library features: - [std::string_view](#stdstring_view) - [std::invoke](#stdinvoke) - [std::apply](#stdapply) +- [std::filesystem](#stdfilesystem) +- [std::byte](#stdbyte) - [splicing for maps and sets](#splicing-for-maps-and-sets) +- [parallel algorithms](#parallel-algorithms) +- [std::sample](#stdsample) +- [std::clamp](#stdclamp) +- [std::reduce](#stdreduce) +- [prefix sum algorithms](#prefix-sum-algorithms) +- [gcd and lcm](#gcd-and-lcm) +- [std::not_fn](#stdnot_fn) +- [string conversion to/from numbers](#string-conversion-tofrom-numbers) +- [rounding functions for chrono durations and timepoints](#rounding-functions-for-chrono-durations-and-timepoints) C++14 includes the following new language features: - [binary literals](#binary-literals) @@ -37,6 +86,7 @@ C++14 includes the following new language features: - [decltype(auto)](#decltypeauto) - [relaxing constraints on constexpr functions](#relaxing-constraints-on-constexpr-functions) - [variable templates](#variable-templates) +- [\[\[deprecated\]\] attribute](#deprecated-attribute) C++14 includes the following new library features: - [user-defined literals for standard library types](#user-defined-literals-for-standard-library-types) @@ -47,12 +97,13 @@ C++11 includes the following new language features: - [move semantics](#move-semantics) - [variadic templates](#variadic-templates) - [rvalue references](#rvalue-references) +- [forwarding references](#forwarding-references) - [initializer lists](#initializer-lists) - [static assertions](#static-assertions) - [auto](#auto) - [lambda expressions](#lambda-expressions) - [decltype](#decltype) -- [template aliases](#template-aliases) +- [type aliases](#type-aliases) - [nullptr](#nullptr) - [strongly-typed enums](#strongly-typed-enums) - [attributes](#attributes) @@ -70,10 +121,16 @@ C++11 includes the following new language features: - [inline-namespaces](#inline-namespaces) - [non-static data member initializers](#non-static-data-member-initializers) - [right angle brackets](#right-angle-brackets) +- [ref-qualified member functions](#ref-qualified-member-functions) +- [trailing return types](#trailing-return-types) +- [noexcept specifier](#noexcept-specifier) +- [char32_t and char16_t](#char32_t-and-char16_t) +- [raw string literals](#raw-string-literals) C++11 includes the following new library features: - [std::move](#stdmove) - [std::forward](#stdforward) +- [std::thread](#stdthread) - [std::to_string](#stdto_string) - [type traits](#type-traits) - [smart pointers](#smart-pointers) @@ -83,7 +140,711 @@ C++11 includes the following new library features: - [std::array](#stdarray) - [unordered containers](#unordered-containers) - [std::make_shared](#stdmake_shared) +- [std::ref](#stdref) - [memory model](#memory-model) +- [std::async](#stdasync) +- [std::begin/end](#stdbeginend) + +## C++20 Language Features + +### Coroutines + +> **Note:** While these examples illustrate how to use coroutines at a basic level, there is lots more going on when the code is compiled. These examples are not meant to be complete coverage of C++20's coroutines. Since the `generator` and `task` classes are not provided by the standard library yet, I used the cppcoro library to compile these examples. + +_Coroutines_ are special functions that can have their execution suspended and resumed. To define a coroutine, the `co_return`, `co_await`, or `co_yield` keywords must be present in the function's body. C++20's coroutines are stackless; unless optimized out by the compiler, their state is allocated on the heap. + +An example of a coroutine is a _generator_ function, which yields (i.e. generates) a value at each invocation: +```c++ +generator range(int start, int end) { + while (start < end) { + co_yield start; + start++; + } + + // Implicit co_return at the end of this function: + // co_return; +} + +for (int n : range(0, 10)) { + std::cout << n << std::endl; +} +``` +The above `range` generator function generates values starting at `start` until `end` (exclusive), with each iteration step yielding the current value stored in `start`. The generator maintains its state across each invocation of `range` (in this case, the invocation is for each iteration in the for loop). `co_yield` takes the given expression, yields (i.e. returns) its value, and suspends the coroutine at that point. Upon resuming, execution continues after the `co_yield`. + +Another example of a coroutine is a _task_, which is an asynchronous computation that is executed when the task is awaited: +```c++ +task echo(socket s) { + for (;;) { + auto data = co_await s.async_read(); + co_await async_write(s, data); + } + + // Implicit co_return at the end of this function: + // co_return; +} +``` +In this example, the `co_await` keyword is introduced. This keyword takes an expression and suspends execution if the thing you're awaiting on (in this case, the read or write) is not ready, otherwise you continue execution. (Note that under the hood, `co_yield` uses `co_await`.) + +Using a task to lazily evaluate a value: +```c++ +task calculate_meaning_of_life() { + co_return 42; +} + +auto meaning_of_life = calculate_meaning_of_life(); +// ... +co_await meaning_of_life; // == 42 +``` + +### Concepts +_Concepts_ are named compile-time predicates which constrain types. They take the following form: +``` +template < template-parameter-list > +concept concept-name = constraint-expression; +``` +where `constraint-expression` evaluates to a constexpr Boolean. _Constraints_ should model semantic requirements, such as whether a type is a numeric or hashable. A compiler error results if a given type does not satisfy the concept it's bound by (i.e. `constraint-expression` returns `false`). Because constraints are evaluated at compile-time, they can provide more meaningful error messages and runtime safety. +```c++ +// `T` is not limited by any constraints. +template +concept always_satisfied = true; +// Limit `T` to integrals. +template +concept integral = std::is_integral_v; +// Limit `T` to both the `integral` constraint and signedness. +template +concept signed_integral = integral && std::is_signed_v; +// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint. +template +concept unsigned_integral = integral && !signed_integral; +``` +There are a variety of syntactic forms for enforcing concepts: +```c++ +// Forms for function parameters: +// `T` is a constrained type template parameter. +template +void f(T v); + +// `T` is a constrained type template parameter. +template + requires my_concept +void f(T v); + +// `T` is a constrained type template parameter. +template +void f(T v) requires my_concept; + +// `v` is a constrained deduced parameter. +void f(my_concept auto v); + +// `v` is a constrained non-type template parameter. +template +void g(); + +// Forms for auto-deduced variables: +// `foo` is a constrained auto-deduced value. +my_concept auto foo = ...; + +// Forms for lambdas: +// `T` is a constrained type template parameter. +auto f = [] (T v) { + // ... +}; +// `T` is a constrained type template parameter. +auto f = [] requires my_concept (T v) { + // ... +}; +// `T` is a constrained type template parameter. +auto f = [] (T v) requires my_concept { + // ... +}; +// `v` is a constrained deduced parameter. +auto f = [](my_concept auto v) { + // ... +}; +// `v` is a constrained non-type template parameter. +auto g = [] () { + // ... +}; +``` +The `requires` keyword is used either to start a `requires` clause or a `requires` expression: +```c++ +template + requires my_concept // `requires` clause. +void f(T); + +template +concept callable = requires (T f) { f(); }; // `requires` expression. + +template + requires requires (T x) { x + x; } // `requires` clause and expression on same line. +T add(T a, T b) { + return a + b; +} +``` +Note that the parameter list in a `requires` expression is optional. Each requirement in a `requires` expression are one of the following: + +* **Simple requirements** - asserts that the given expression is valid. + +```c++ +template +concept callable = requires (T f) { f(); }; +``` +* **Type requirements** - denoted by the `typename` keyword followed by a type name, asserts that the given type name is valid. + +```c++ +struct foo { + int foo; +}; + +struct bar { + using value = int; + value data; +}; + +struct baz { + using value = int; + value data; +}; + +// Using SFINAE, enable if `T` is a `baz`. +template >> +struct S {}; + +template +using Ref = T&; + +template +concept C = requires { + // Requirements on type `T`: + typename T::value; // A) has an inner member named `value` + typename S; // B) must have a valid class template specialization for `S` + typename Ref; // C) must be a valid alias template substitution +}; + +template +void g(T a); + +g(foo{}); // ERROR: Fails requirement A. +g(bar{}); // ERROR: Fails requirement B. +g(baz{}); // PASS. +``` +* **Compound requirements** - an expression in braces followed by a trailing return type or type constraint. + +```c++ +template +concept C = requires(T x) { + {*x} -> std::convertible_to; // the type of the expression `*x` is convertible to `T::inner` + {x + 1} -> std::same_as; // the expression `x + 1` satisfies `std::same_as` + {x * 1} -> std::convertible_to; // the type of the expression `x * 1` is convertible to `T` +}; +``` +* **Nested requirements** - denoted by the `requires` keyword, specify additional constraints (such as those on local parameter arguments). + +```c++ +template +concept C = requires(T x) { + requires std::same_as; +}; +``` +See also: [concepts library](#concepts-library). + +### Three-way comparison +C++20 introduces the spaceship operator (`<=>`) as a new way to write comparison functions that reduce boilerplate and help developers define clearer comparison semantics. Defining a three-way comparison operator will autogenerate the other comparison operator functions (i.e. `==`, `!=`, `<`, etc.). + +Three orderings are introduced: +* `std::strong_ordering`: The strong ordering distinguishes between items being equal (identical and interchangeable). Provides `less`, `greater`, `equivalent`, and `equal` ordering. Examples of comparisons: searching for a specific value in a list, values of integers, case-sensitive strings. +* `std::weak_ordering`: The weak ordering distinguishes between items being equivalent (not identical, but can be interchangeable for the purposes of comparison). Provides `less`, `greater`, and `equivalent` ordering. Examples of comparisons: case-insensitive strings, sorting, comparing some but not all visible members of a class. +* `std::partial_ordering`: The partial ordering follows the same principle of weak ordering but includes the case when an ordering isn't possible. Provides `less`, `greater`, `equivalent`, and `unordered` ordering. Examples of comparisons: floating-point values (e.g. `NaN`). + +A defaulted three-way comparison operator does a member-wise comparison: +```c++ +struct foo { + int a; + bool b; + char c; + + // Compare `a` first, then `b`, then `c` ... + friend auto operator<=>(const foo&) const = default; +}; + +foo f1{0, false, 'a'}, f2{0, true, 'b'}; +f1 < f2; // == true +f1 == f2; // == false +f1 >= f2; // == false +``` + +You can also define your own comparisons: +```c++ +struct foo { + int x; + bool b; + char c; + + friend std::strong_ordering operator<=>(const foo& other) const { + return x <=> other.x; + } +}; + +foo f1{0, false, 'a'}, f2{0, true, 'b'}; +f1 < f2; // == false +f1 == f2; // == true +f1 >= f2; // == true +``` + +### Designated initializers +C-style designated initializer syntax. Any member fields that are not explicitly listed in the designated initializer list are default-initialized. +```c++ +struct A { + int x; + int y; + int z = 123; +}; + +A a {.x = 1, .z = 2}; // a.x == 1, a.y == 0, a.z == 2 +``` + +### Template syntax for lambdas +Use familiar template syntax in lambda expressions. +```c++ +auto f = [](std::vector v) { + // ... +}; +``` + +### Range-based for loop with initializer +This feature simplifies common code patterns, helps keep scopes tight, and offers an elegant solution to a common lifetime problem. +```c++ +for (auto v = std::vector{1, 2, 3}; auto& e : v) { + std::cout << e; +} +// prints "123" +``` + +### \[\[likely\]\] and \[\[unlikely\]\] attributes +Provides a hint to the optimizer that the labelled statement has a high probability of being executed. +```c++ +switch (n) { +case 1: + // ... + break; + +[[likely]] case 2: // n == 2 is considered to be arbitrarily more + // ... // likely than any other value of n + break; +} +``` + +If one of the likely/unlikely attributes appears after the right parenthesis of an if-statement, +it indicates that the branch is likely/unlikely to have its substatement (body) executed. +```c++ +int random = get_random_number_between_x_and_y(0, 3); +if (random > 0) [[likely]] { + // body of if statement + // ... +} +``` + +It can also be applied to the substatement (body) of an iteration statement. +```c++ +while (unlikely_truthy_condition) [[unlikely]] { + // body of while statement + // ... +} +``` + +### Deprecate implicit capture of this +Implicitly capturing `this` in a lambda capture using `[=]` is now deprecated; prefer capturing explicitly using `[=, this]` or `[=, *this]`. +```c++ +struct int_value { + int n = 0; + auto getter_fn() { + // BAD: + // return [=]() { return n; }; + + // GOOD: + return [=, *this]() { return n; }; + } +}; +``` + +### Class types in non-type template parameters +Classes can now be used in non-type template parameters. Objects passed in as template arguments have the type `const T`, where `T` is the type of the object, and has static storage duration. +```c++ +struct foo { + foo() = default; + constexpr foo(int) {} +}; + +template +auto get_foo() { + return f; +} + +get_foo(); // uses implicit constructor +get_foo(); +``` + +### constexpr virtual functions +Virtual functions can now be `constexpr` and evaluated at compile-time. `constexpr` virtual functions can override non-`constexpr` virtual functions and vice-versa. +```c++ +struct X1 { + virtual int f() const = 0; +}; + +struct X2: public X1 { + constexpr virtual int f() const { return 2; } +}; + +struct X3: public X2 { + virtual int f() const { return 3; } +}; + +struct X4: public X3 { + constexpr virtual int f() const { return 4; } +}; + +constexpr X4 x4; +x4.f(); // == 4 +``` + +### explicit(bool) +Conditionally select at compile-time whether a constructor is made explicit or not. `explicit(true)` is the same as specifying `explicit`. +```c++ +struct foo { + // Specify non-integral types (strings, floats, etc.) require explicit construction. + template + explicit(!std::is_integral_v) foo(T) {} +}; + +foo a = 123; // OK +foo b = "123"; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true) +foo c {"123"}; // OK +``` + +### Immediate functions +Similar to `constexpr` functions, but functions with a `consteval` specifier must produce a constant. These are called `immediate functions`. +```c++ +consteval int sqr(int n) { + return n * n; +} + +constexpr int r = sqr(100); // OK +int x = 100; +int r2 = sqr(x); // ERROR: the value of 'x' is not usable in a constant expression + // OK if `sqr` were a `constexpr` function +``` + +### using enum +Bring an enum's members into scope to improve readability. Before: +```c++ +enum class rgba_color_channel { red, green, blue, alpha }; + +std::string_view to_string(rgba_color_channel channel) { + switch (channel) { + case rgba_color_channel::red: return "red"; + case rgba_color_channel::green: return "green"; + case rgba_color_channel::blue: return "blue"; + case rgba_color_channel::alpha: return "alpha"; + } +} +``` +After: +```c++ +enum class rgba_color_channel { red, green, blue, alpha }; + +std::string_view to_string(rgba_color_channel my_channel) { + switch (my_channel) { + using enum rgba_color_channel; + case red: return "red"; + case green: return "green"; + case blue: return "blue"; + case alpha: return "alpha"; + } +} +``` + +### Lambda capture of parameter pack +Capture parameter packs by value: +```c++ +template +auto f(Args&&... args){ + // BY VALUE: + return [...args = std::forward(args)] { + // ... + }; +} +``` +Capture parameter packs by reference: +```c++ +template +auto f(Args&&... args){ + // BY REFERENCE: + return [&...args = std::forward(args)] { + // ... + }; +} +``` + +### char8_t +Provides a standard type for representing UTF-8 strings. +```c++ +char8_t utf8_str[] = u8"\u0123"; +``` + +### constinit +The `constinit` specifier requires that a variable must be initialized at compile-time. +```c++ +const char* g() { return "dynamic initialization"; } +constexpr const char* f(bool p) { return p ? "constant initializer" : g(); } + +constinit const char* c = f(true); // OK +constinit const char* d = g(false); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time. +``` + +### \_\_VA\_OPT\_\_ +Helps support variadic macros by evaluating to the given argument if the variadic macro is non-empty. +```c++ +#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__) +F(a, b, c) // replaced by f(0, a, b, c) +F() // replaced by f(0) +``` + +## C++20 Library Features + +### Concepts library +Concepts are also provided by the standard library for building more complicated concepts. Some of these include: + +**Core language concepts:** +- `same_as` - specifies two types are the same. +- `derived_from` - specifies that a type is derived from another type. +- `convertible_to` - specifies that a type is implicitly convertible to another type. +- `common_with` - specifies that two types share a common type. +- `integral` - specifies that a type is an integral type. +- `default_constructible` - specifies that an object of a type can be default-constructed. + + **Comparison concepts:** +- `boolean` - specifies that a type can be used in Boolean contexts. +- `equality_comparable` - specifies that `operator==` is an equivalence relation. + + **Object concepts:** +- `movable` - specifies that an object of a type can be moved and swapped. +- `copyable` - specifies that an object of a type can be copied, moved, and swapped. +- `semiregular` - specifies that an object of a type can be copied, moved, swapped, and default constructed. +- `regular` - specifies that a type is _regular_, that is, it is both `semiregular` and `equality_comparable`. + + **Callable concepts:** +- `invocable` - specifies that a callable type can be invoked with a given set of argument types. +- `predicate` - specifies that a callable type is a Boolean predicate. + +See also: [concepts](#concepts). + +### Formatting library +Combine the simplicity of `printf` with the type-safety of `iostream`. Uses braces as placeholders, and supports custom formatting similar to printf-style specifiers. +```c++ +std::format("{1} {0}", "world", "hello"); // == "hello world" + +int x = 123; +std::string str = std::format("x: {}", x); // str == "x: 123" + +// Format to an output iterator: +for (auto x : {1, 2, 3}) { + std::format_to(std::ostream_iterator{std::cout, "\n"}, "{}", x); +} +``` + +To format custom types: +```c++ +struct fraction { + int numerator; + int denominator; +}; + +template <> +struct std::formatter +{ + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + auto format(const fraction& f, std::format_context& ctx) const { + return std::format_to(ctx.out(), "{0:d}/{1:d}", f.numerator, f.denominator); + } +}; + +fraction f{1, 2}; +std::format("{}", f); // == "1/2" +``` + +### Synchronized buffered outputstream +Buffers output operations for the wrapped output stream ensuring synchronization (i.e. no interleaving of output). +```c++ +std::osyncstream{std::cout} << "The value of x is:" << x << std::endl; +``` + +### std::span +A span is a view (i.e. non-owning) of a container providing bounds-checked access to a contiguous group of elements. Since views do not own their elements they are cheap to construct and copy -- a simplified way to think about views is they are holding references to their data. As opposed to maintaining a pointer/iterator and length field, a span wraps both of those up in a single object. + +Spans can be dynamically-sized or fixed-sized (known as their *extent*). Fixed-sized spans benefit from bounds-checking. + +Span doesn't propogate const so to construct a read-only span use `std::span`. + +Example: using a dynamically-sized span to print integers from various containers. +```c++ +void print_ints(std::span ints) { + for (const auto n : ints) { + std::cout << n << std::endl; + } +} + +print_ints(std::vector{ 1, 2, 3 }); +print_ints(std::array{ 1, 2, 3, 4, 5 }); + +int a[10] = { 0 }; +print_ints(a); +// etc. +``` + +Example: a statically-sized span will fail to compile for containers that don't match the extent of the span. +```c++ +void print_three_ints(std::span ints) { + for (const auto n : ints) { + std::cout << n << std::endl; + } +} + +print_three_ints(std::vector{ 1, 2, 3 }); // ERROR +print_three_ints(std::array{ 1, 2, 3, 4, 5 }); // ERROR +int a[10] = { 0 }; +print_three_ints(a); // ERROR + +std::array b = { 1, 2, 3 }; +print_three_ints(b); // OK + +// You can construct a span manually if required: +std::vector c{ 1, 2, 3 }; +print_three_ints(std::span{ c.data(), 3 }); // OK: set pointer and length field. +print_three_ints(std::span{ c.cbegin(), c.cend() }); // OK: use iterator pairs. +``` + +### Bit operations +C++20 provides a new `` header which provides some bit operations including popcount. +```c++ +std::popcount(0u); // 0 +std::popcount(1u); // 1 +std::popcount(0b1111'0000u); // 4 +``` + +### Math constants +Mathematical constants including PI, Euler's number, etc. defined in the `` header. +```c++ +std::numbers::pi; // 3.14159... +std::numbers::e; // 2.71828... +``` + +### std::is_constant_evaluated +Predicate function which is truthy when it is called in a compile-time context. +```c++ +constexpr bool is_compile_time() { + return std::is_constant_evaluated(); +} + +constexpr bool a = is_compile_time(); // true +bool b = is_compile_time(); // false +``` + +### std::make_shared supports arrays +```c++ +auto p = std::make_shared(5); // pointer to `int[5]` +// OR +auto p = std::make_shared(); // pointer to `int[5]` +``` + +### starts_with and ends_with on strings +Strings (and string views) now have the `starts_with` and `ends_with` member functions to check if a string starts or ends with the given string. +```c++ +std::string str = "foobar"; +str.starts_with("foo"); // true +str.ends_with("baz"); // false +``` + +### Check if associative container has element +Associative containers such as sets and maps have a `contains` member function, which can be used instead of the "find and check end of iterator" idiom. +```c++ +std::map map {{1, 'a'}, {2, 'b'}}; +map.contains(2); // true +map.contains(123); // false + +std::set set {1, 2, 3}; +set.contains(2); // true +``` + +### std::bit_cast +A safer way to reinterpret an object from one type to another. +```c++ +float f = 123.0; +int i = std::bit_cast(f); +``` + +### std::midpoint +Calculate the midpoint of two integers safely (without overflow). +```c++ +std::midpoint(1, 3); // == 2 +``` + +### std::to_array +Converts the given array/"array-like" object to a `std::array`. +```c++ +std::to_array("foo"); // returns `std::array` +std::to_array({1, 2, 3}); // returns `std::array` + +int a[] = {1, 2, 3}; +std::to_array(a); // returns `std::array` +``` + +### std::bind_front +Binds the first N arguments (where N is the number of arguments after the given function to `std::bind_front`) to a given free function, lambda, or member function. +```c++ +const auto f = [](int a, int b, int c) { return a + b + c; }; +const auto g = std::bind_front(f, 1, 1); +g(1); // == 3 +``` + +### Uniform container erasure +Provides `std::erase` and/or `std::erase_if` for a variety of STL containers such as string, list, vector, map, etc. + +For erasing by value use `std::erase`, or to specify a predicate when to erase elements use `std::erase_if`. Both functions return the number of erased elements. + +```c++ +std::vector v{0, 1, 0, 2, 0, 3}; +std::erase(v, 0); // v == {1, 2, 3} +std::erase_if(v, [](int n) { return n == 0; }); // v == {1, 2, 3} +``` + +### Three-way comparison helpers +Helper functions for giving names to comparison results: +```c++ +std::is_eq(0 <=> 0); // == true +std::is_lteq(0 <=> 1); // == true +std::is_gt(0 <=> 1); // == false +``` + +See also: [three-way comparison](#three-way-comparison). + +### std::lexicographical_compare_three_way +Lexicographically compares two ranges using three-way comparison and produces a result of the strongest applicable comparison category type. +```c++ +std::vector a{0, 0, 0}, b{0, 0, 0}, c{1, 1, 1}; + +auto cmp_ab = std::lexicographical_compare_three_way( + a.begin(), a.end(), b.begin(), b.end()); +std::is_eq(cmp_ab); // == true + +auto cmp_ac = std::lexicographical_compare_three_way( + a.begin(), a.end(), c.begin(), c.end()); +std::is_lt(cmp_ac); // == true +``` + +See also: [three-way comparison](#three-way-comparison), [three-way comparison helpers](#three-way-comparison-helpers). ## C++17 Language Features @@ -93,18 +854,18 @@ Automatic template argument deduction much like how it's done for functions, but template struct MyContainer { T val; - MyContainer() : val() {} - MyContainer(T val) : val(val) {} + MyContainer() : val{} {} + MyContainer(T val) : val{val} {} // ... }; -MyContainer c1{ 1 }; // OK MyContainer +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 +template struct my_integer_sequence { // Implementation here ... }; @@ -119,9 +880,9 @@ auto seq2 = my_integer_sequence<0, 1, 2>(); ### 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. +* An expression of the form `(e1 op ... op e2)`, where `op` are fold-operators, is called a _binary fold_. Either `e1` or `e2` is an unexpanded parameter pack, but not both. ```c++ -template +template bool logicalAnd(Args... args) { // Binary folding. return (true && ... && args); @@ -131,7 +892,7 @@ bool& b2 = b; logicalAnd(b, b2, true); // == true ``` ```c++ -template +template auto sum(Args... args) { // Unary folding. return (... + args); @@ -140,22 +901,22 @@ 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`. +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 +auto x1 {1, 2, 3}; // error: not a single element +auto x2 = {1, 2, 3}; // x2 is std::initializer_list +auto x3 {3}; // x3 is int +auto x4 {3.0}; // x4 is double ``` ### constexpr lambda Compile-time lambdas using `constexpr`. ```c++ -auto identity = [] (int n) constexpr { return n; }; +auto identity = [](int n) constexpr { return n; }; static_assert(identity(123) == 123); ``` ```c++ -constexpr auto add = [] (int x, int y) { +constexpr auto add = [](int x, int y) { auto L = [=] { return x; }; auto R = [=] { return y; }; return [=] { return L() + R(); }; @@ -175,7 +936,7 @@ static_assert(addOne(1) == 2); 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 }; + int value {123}; auto getValueCopy() { return [*this] { return value; }; } @@ -204,6 +965,16 @@ S x2 = S{123}; // mov eax, dword ptr [.L_ZZ4mainE2x2] // .L_ZZ4mainE2x2: .long 123 ``` +It can also be used to declare and define a static member variable, such that it does not need to be initialized in the source file. +```c++ +struct S { + S() : id{count++} {} + ~S() { count--; } + int id; + static inline int count{0}; // declare and initialize count to 0 within the class +}; +``` + ### Nested namespaces Using the namespace resolution operator to create nested namespace definitions. ```c++ @@ -214,7 +985,10 @@ namespace A { } } } -// vs. +``` + +The code above can be written like this: +```c++ namespace A::B::C { int i; } @@ -232,6 +1006,18 @@ const auto [ x, y ] = origin(); x; // == 0 y; // == 0 ``` +```c++ +std::unordered_map mapping { + {"a", 1}, + {"b", 2}, + {"c", 3} +}; + +// Destructure by reference. +for (const auto& [key, value] : mapping) { + // Do something with key and value +} +``` ### Selection statements with initializer New versions of the `if` and `switch` statements which simplify common code patterns and help users keep scopes tight. @@ -276,22 +1062,144 @@ struct S {}; static_assert(isIntegral() == false); ``` -### UTF-8 Character Literals +### 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 +### 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 b {0}; // OK +byte c {-1}; // ERROR byte d = byte{1}; // OK byte e = byte{256}; // ERROR ``` +### \[\[fallthrough\]\], \[\[nodiscard\]\], \[\[maybe_unused\]\] attributes +C++17 introduces three new attributes: `[[fallthrough]]`, `[[nodiscard]]` and `[[maybe_unused]]`. +* `[[fallthrough]]` indicates to the compiler that falling through in a switch statement is intended behavior. This attribute may only be used in a switch statement, and must be placed before the next case/default label. +```c++ +switch (n) { + case 1: + // ... + [[fallthrough]]; + case 2: + // ... + break; + case 3: + // ... + [[fallthrough]]; + default: + // ... +} +``` + +* `[[nodiscard]]` issues a warning when either a function or class has this attribute and its return value is discarded. +```c++ +[[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' +``` +```c++ +// 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]]` indicates to the compiler that a variable or parameter might be unused and is intended. +```c++ +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 (operand)` operator may be used in `#if` and `#elif` expressions to check whether a header or source file (`operand`) is available for inclusion or not. + +One use case of this would be using two libraries that work the same way, using the backup/experimental one if the preferred one is not found on the system. + +```c++ +#ifdef __has_include +# if __has_include() +# include +# define have_optional 1 +# elif __has_include() +# include +# define have_optional 1 +# define experimental_optional +# else +# define have_optional 0 +# endif +#endif +``` + +It can also be used to include headers existing under different names or locations on various platforms, without knowing which platform the program is running on, OpenGL headers are a good example for this which are located in `OpenGL\` directory on macOS and `GL\` on other platforms. + +```c++ +#ifdef __has_include +# if __has_include() +# include +# include +# elif __has_include() +# include +# include +# else +# error No suitable OpenGL headers found. +# endif +#endif +``` + +### Class template argument deduction +*Class template argument deduction* (CTAD) allows the compiler to deduce template arguments from constructor arguments. +```c++ +std::vector v{ 1, 2, 3 }; // deduces std::vector + +std::mutex mtx; +auto lck = std::lock_guard{ mtx }; // deduces to std::lock_guard + +auto p = new std::pair{ 1.0, 2.0 }; // deduces to std::pair* +``` + +For user-defined types, *deduction guides* can be used to guide the compiler how to deduce template arguments if applicable: +```c++ +template +struct container { + container(T t) {} + + template + container(Iter beg, Iter end); +}; + +// deduction guide +template +container(Iter b, Iter e) -> container::value_type>; + +container a{ 7 }; // OK: deduces container + +std::vector v{ 1.0, 2.0, 3.0 }; +auto b = container{ v.begin(), v.end() }; // OK: deduces container + +container c{ 5, 6 }; // ERROR: std::iterator_traits::value_type is not a type +``` + ## C++17 Library Features ### std::variant @@ -327,7 +1235,7 @@ if (auto str = create(true)) { ### std::any A type-safe container for single values of any type. ```c++ -std::any x{ 5 }; +std::any x {5}; x.has_value() // == true std::any_cast(x) // == 5 std::any_cast(x) = 10; @@ -338,58 +1246,86 @@ std::any_cast(x) // == 10 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" }; +std::string_view cppstr {"foo"}; // Wide strings. -std::wstring_view wcstr_v{ L"baz" }; +std::wstring_view wcstr_v {L"baz"}; // Character arrays. char array[3] = {'b', 'a', 'r'}; -std::string_view array_v(array, sizeof array); +std::string_view array_v(array, std::size(array)); ``` ```c++ -std::string str{ " trim me" }; -std::string_view v{ str }; +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. +Invoke a `Callable` object with parameters. Examples of *callable* objects are `std::function` or lambdas; objects that can be called similarly to a regular function. ```c++ template class Proxy { - Callable c; + 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(Callable c) : c_{ std::move(c) } {} + + template + decltype(auto) operator()(Args&&... args) { + // ... + return std::invoke(c_, std::forward(args)...); + } }; -Proxy p{ add }; + +const 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) { +auto add = [](int x, int y) { return x + y; }; -std::apply(add, std::make_tuple( 1, 2 )); // == 3 +std::apply(add, std::make_tuple(1, 2)); // == 3 +``` + +### 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")); + } +} +``` + +### std::byte +The new `std::byte` type provides a standard way of representing data as a byte. Benefits of using `std::byte` over `char` or `unsigned char` is that it is not a character type, and is also not an arithmetic type; while the only operator overloads available are bitwise operations. +```c++ +std::byte a {0}; +std::byte b {0xFF}; +int i = std::to_integer(b); // 0xFF +std::byte c = a & b; +int j = std::to_integer(c); // 0 ``` +Note that `std::byte` is simply an enum, and braced initialization of enums become possible thanks to [direct-list-initialization of enums](#direct-list-initialization-of-enums). ### 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" } }; +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" } }; @@ -397,8 +1333,8 @@ dst.insert(src.extract(2)); // Cheap remove and insert of { 2, "two" } from `src Inserting an entire set: ```c++ -std::set src{1, 3, 5}; -std::set dst{2, 4, 5}; +std::set src {1, 3, 5}; +std::set dst {2, 4, 5}; dst.merge(src); // src == { 5 } // dst == { 1, 2, 3, 4, 5 } @@ -416,13 +1352,157 @@ s2.insert(elementFactory()); Changing the key of a map element: ```c++ -std::map m{ { 1, "one" }, { 2, "two" }, { 3, "three" } }; +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" } } ``` +### Parallel algorithms +Many of the STL algorithms, such as the `copy`, `find` and `sort` methods, started to support the *parallel execution policies*: `seq`, `par` and `par_unseq` which translate to "sequentially", "parallel" and "parallel unsequenced". + +```c++ +std::vector 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)); +``` + +### std::sample +Samples n elements in the given sequence (without replacement) where every element has an equal chance of being selected. +```c++ +const std::string ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +std::string guid; +// Sample 5 characters from ALLOWED_CHARS. +std::sample(ALLOWED_CHARS.begin(), ALLOWED_CHARS.end(), std::back_inserter(guid), + 5, std::mt19937{ std::random_device{}() }); + +std::cout << guid; // e.g. G1fW2 +``` + +### std::clamp +Clamp given value between a lower and upper bound. +```c++ +std::clamp(42, -1, 1); // == 1 +std::clamp(-42, -1, 1); // == -1 +std::clamp(0, -1, 1); // == 0 + +// `std::clamp` also accepts a custom comparator: +std::clamp(0, -1, 1, std::less<>{}); // == 0 +``` + +### std::reduce +Fold over a given range of elements. Conceptually similar to `std::accumulate`, but `std::reduce` will perform the fold in parallel. Due to the fold being done in parallel, if you specify a binary operation, it is required to be associative and commutative. A given binary operation also should not change any element or invalidate any iterators within the given range. + +The default binary operation is std::plus with an initial value of 0. +```c++ +const std::array a{ 1, 2, 3 }; +std::reduce(std::cbegin(a), std::cend(a)); // == 6 +// Using a custom binary op: +std::reduce(std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6 +``` +Additionally you can specify transformations for reducers: +```c++ +std::transform_reduce(std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten); // == 60 + +const std::array b{ 1, 2, 3 }; +const auto product_times_ten = [](const auto a, const auto b) { return a * b * 10; }; + +std::transform_reduce(std::cbegin(a), std::cend(a), std::cbegin(b), 0, std::plus<>{}, product_times_ten); // == 140 +``` + +### Prefix sum algorithms +Support for prefix sums (both inclusive and exclusive scans) along with transformations. +```c++ +const std::array a{ 1, 2, 3 }; + +std::inclusive_scan(std::cbegin(a), std::cend(a), + std::ostream_iterator{ std::cout, " " }, std::plus<>{}); // 1 3 6 + +std::exclusive_scan(std::cbegin(a), std::cend(a), + std::ostream_iterator{ std::cout, " " }, 0, std::plus<>{}); // 0 1 3 + +const auto times_ten = [](const auto n) { return n * 10; }; + +std::transform_inclusive_scan(std::cbegin(a), std::cend(a), + std::ostream_iterator{ std::cout, " " }, std::plus<>{}, times_ten); // 10 30 60 + +std::transform_exclusive_scan(std::cbegin(a), std::cend(a), + std::ostream_iterator{ std::cout, " " }, 0, std::plus<>{}, times_ten); // 0 10 30 +``` + +### GCD and LCM +Greatest common divisor (GCD) and least common multiple (LCM). +```c++ +const int p = 9; +const int q = 3; +std::gcd(p, q); // == 3 +std::lcm(p, q); // == 9 +``` + +### std::not_fn +Utility function that returns the negation of the result of the given function. +```c++ +const std::ostream_iterator ostream_it{ std::cout, " " }; +const auto is_even = [](const auto n) { return n % 2 == 0; }; +std::vector v{ 0, 1, 2, 3, 4 }; + +// Print all even numbers. +std::copy_if(std::cbegin(v), std::cend(v), ostream_it, is_even); // 0 2 4 +// Print all odd (not even) numbers. +std::copy_if(std::cbegin(v), std::cend(v), ostream_it, std::not_fn(is_even)); // 1 3 +``` + +### String conversion to/from numbers +Convert integrals and floats to a string or vice-versa. Conversions are non-throwing, do not allocate, and are more secure than the equivalents from the C standard library. + +Users are responsible for allocating enough storage required for `std::to_chars`, or the function will fail by setting the error code object in its return value. + +These functions allow you to optionally pass a base (defaults to base-10) or a format specifier for floating type input. + +* `std::to_chars` returns a (non-const) char pointer which is one-past-the-end of the string that the function wrote to inside the given buffer, and an error code object. +* `std::from_chars` returns a const char pointer which on success is equal to the end pointer passed to the function, and an error code object. + +Both error code objects returned from these functions are equal to the default-initialized error code object on success. + +Convert the number `123` to a `std::string`: +```c++ +const int n = 123; + +// Can use any container, string, array, etc. +std::string str; +str.resize(3); // hold enough storage for each digit of `n` + +const auto [ ptr, ec ] = std::to_chars(str.data(), str.data() + str.size(), n); + +if (ec == std::errc{}) { std::cout << str << std::endl; } // 123 +else { /* handle failure */ } +``` + +Convert from a `std::string` with value `"123"` to an integer: +```c++ +const std::string str{ "123" }; +int n; + +const auto [ ptr, ec ] = std::from_chars(str.data(), str.data() + str.size(), n); + +if (ec == std::errc{}) { std::cout << n << std::endl; } // 123 +else { /* handle failure */ } +``` + +### Rounding functions for chrono durations and timepoints +Provides abs, round, ceil, and floor helper functions for `std::chrono::duration` and `std::chrono::time_point`. +```c++ +using seconds = std::chrono::seconds; +std::chrono::milliseconds d{ 5500 }; +std::chrono::abs(d); // == 5s +std::chrono::round(d); // == 6s +std::chrono::ceil(d); // == 6s +std::chrono::floor(d); // == 5s +``` + ## C++14 Language Features ### Binary literals @@ -495,7 +1575,7 @@ int& z = g(y); // reference to `y` ``` ### decltype(auto) -The `decltype(auto)` type-specifier also deduces a type like `auto` does. However, it deduces return types while keeping their references or "const-ness", while `auto` will not. +The `decltype(auto)` type-specifier also deduces a type like `auto` does. However, it deduces return types while keeping their references and cv-qualifiers, while `auto` will not. ```c++ const int x = 0; auto x1 = x; // int @@ -527,6 +1607,8 @@ static_assert(std::is_same::value == 1); static_assert(std::is_same::value == 1); ``` +See also: [`decltype (C++11)`](#decltype). + ### Relaxing constraints on constexpr functions In C++11, `constexpr` function bodies could only contain a very limited set of syntaxes, including (but not limited to): `typedef`s, `using`s, and a single `return` statement. In C++14, the set of allowable syntaxes expands greatly to include the most common syntax such as `if` statements, multiple `return`s, loops, etc. ```c++ @@ -540,7 +1622,7 @@ constexpr int factorial(int n) { factorial(5); // == 120 ``` -### Variable Templates +### Variable templates C++14 allows variables to be templated: ```c++ @@ -550,6 +1632,15 @@ template constexpr T e = T(2.7182818284590452353); ``` +### [[deprecated]] attribute +C++14 introduces the `[[deprecated]]` attribute to indicate that a unit (function, class, etc.) is discouraged and likely yield compilation warnings. If a reason is provided, it will be included in the warnings. +```c++ +[[deprecated]] +void old_method(); +[[deprecated("Use new_method instead")]] +void legacy_method(); +``` + ## C++14 Library Features ### User-defined literals for standard library types @@ -563,7 +1654,7 @@ std::chrono::duration_cast(day).count(); // == 1440 ### Compile-time integer sequences The class template `std::integer_sequence` represents a compile-time sequence of integers. There are a few helpers built on top: -* `std::make_integer_sequence` - creates a sequence of `0, ..., N - 1` with type `T`. +* `std::make_integer_sequence` - creates a sequence of `0, ..., N - 1` with type `T`. * `std::index_sequence_for` - converts a template parameter pack into an integer sequence. Convert an array into a tuple: @@ -585,41 +1676,92 @@ decltype(auto) a2t(const std::array& a) { * Prevents code repetition when specifying the underlying type the pointer shall hold. * Most importantly, it provides exception-safety. Suppose we were calling a function `foo` like so: ```c++ -foo(std::unique_ptr{ new T{} }, function_that_throws(), std::unique_ptr{ new T{} }); +foo(std::unique_ptr{new T{}}, function_that_throws(), std::unique_ptr{new T{}}); ``` The compiler is free to call `new T{}`, then `function_that_throws()`, and so on... Since we have allocated data on the heap in the first construction of a `T`, we have introduced a leak here. With `std::make_unique`, we are given exception-safety: ```c++ foo(std::make_unique(), function_that_throws(), std::make_unique()); ``` -See the section on [smart pointers](#smart-pointers) for more information on `std::unique_ptr` and `std::shared_ptr`. +See the section on [smart pointers (C++11)](#smart-pointers) for more information on `std::unique_ptr` and `std::shared_ptr`. ## C++11 Language Features ### Move semantics -Move semantics is mostly about performance optimization: the ability to move an object without the expensive overhead of copying. The difference between a copy and a move is that a copy leaves the source unchanged, and a move will leave the source either unchanged or radically different -- depending on what the source is. For plain old data, a move is the same as a copy. +Moving an object means to transfer ownership of some resource it manages to another object. -To move an object means to transfer ownership of some resource it manages to another object. You could think of this as changing pointers held by the source object to be moved, or now held, by the destination object; the resource remains in its location in memory. Such an inexpensive transfer of resources is extremely useful when the source is an `rvalue`, where the potentially dangerous side-effect of changing the source after the move is redundant since the source is a temporary object that won't be accessible later. +The first benefit of move semantics is performance optimization. When an object is about to reach the end of its lifetime, either because it's a temporary or by explicitly calling `std::move`, a move is often a cheaper way to transfer resources. For example, moving a `std::vector` is just copying some pointers and internal state over to the new vector -- copying would involve having to copy every single contained element in the vector, which is expensive and unnecessary if the old vector will soon be destroyed. -Moves also make it possible to transfer objects such as `std::unique_ptr`s, [smart pointers](#smart-pointers) that are designed to hold a pointer to a unique object, from one scope to another. +Moves also make it possible for non-copyable types such as `std::unique_ptr`s ([smart pointers](#smart-pointers)) to guarantee at the language level that there is only ever one instance of a resource being managed at a time, while being able to transfer an instance between scopes. -See the sections on: [rvalue references](#rvalue-references), [defining move special member functions](#special-member-functions-for-move-semantics), [`std::move`](#stdmove), [`std::forward`](#stdforward). +See the sections on: [rvalue references](#rvalue-references), [special member functions for move semantics](#special-member-functions-for-move-semantics), [`std::move`](#stdmove), [`std::forward`](#stdforward), [`forwarding references`](#forwarding-references). ### Rvalue references -C++11 introduces a new reference termed the _rvalue reference_. An rvalue reference to `A` is created with the syntax `A&&`. This enables two major features: move semantics; and _perfect forwarding_, the ability to pass arguments while maintaining information about them as lvalues/rvalues in a generic way. +C++11 introduces a new reference termed the _rvalue reference_. An rvalue reference to `T`, which is a non-template type parameter (such as `int`, or a user-defined type), is created with the syntax `T&&`. Rvalue references only bind to rvalues. -`auto` type deduction with lvalues and rvalues: +Type deduction with lvalues and rvalues: ```c++ int x = 0; // `x` is an lvalue of type `int` int& xl = x; // `xl` is an lvalue of type `int&` int&& xr = x; // compiler error -- `x` is an lvalue -int&& xr2 = 0; // `xr2` is an lvalue of type `int&&` -auto& al = x; // `al` is an lvalue of type `int&` -auto&& al2 = x; // `al2` is an lvalue of type `int&` -auto&& ar = 0; // `ar` is an lvalue of type `int&&` +int&& xr2 = 0; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0` + +void f(int& x) {} +void f(int&& x) {} + +f(x); // calls f(int&) +f(xl); // calls f(int&) +f(3); // calls f(int&&) +f(std::move(x)); // calls f(int&&) + +f(xr2); // calls f(int&) +f(std::move(xr2)); // calls f(int&& x) +``` + +See also: [`std::move`](#stdmove), [`std::forward`](#stdforward), [`forwarding references`](#forwarding-references). + +### Forwarding references +Also known (unofficially) as _universal references_. A forwarding reference is created with the syntax `T&&` where `T` is a template type parameter, or using `auto&&`. This enables _perfect forwarding_: the ability to pass arguments while maintaining their value category (e.g. lvalues stay as lvalues, temporaries are forwarded as rvalues). + +Forwarding references allow a reference to bind to either an lvalue or rvalue depending on the type. Forwarding references follow the rules of _reference collapsing_: +* `T& &` becomes `T&` +* `T& &&` becomes `T&` +* `T&& &` becomes `T&` +* `T&& &&` becomes `T&&` + +`auto` type deduction with lvalues and rvalues: +```c++ +int x = 0; // `x` is an lvalue of type `int` +auto&& al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x` +auto&& ar = 0; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0` +``` + +Template type parameter deduction with lvalues and rvalues: +```c++ +// Since C++14 or later: +void f(auto&& t) { + // ... +} + +// Since C++11 or later: +template +void f(T&& t) { + // ... +} + +int x = 0; +f(0); // T is int, deduces as f(int &&) => f(int&&) +f(x); // T is int&, deduces as f(int& &&) => f(int&) + +int& y = x; +f(y); // T is int&, deduces as f(int& &&) => f(int&) + +int&& z = 0; // NOTE: `z` is an lvalue with type `int&&`. +f(z); // T is int&, deduces as f(int& &&) => f(int&) +f(std::move(z)); // T is int, deduces as f(int &&) => f(int&&) ``` -See also: [`std::move`](#stdmove), [`std::forward`](#stdforward). +See also: [`std::move`](#stdmove), [`std::forward`](#stdforward), [`rvalue references`](#rvalue-references). ### Variadic templates The `...` syntax creates a _parameter pack_ or expands one. A template _parameter pack_ is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A template with at least one parameter pack is called a _variadic template_. @@ -632,6 +1774,19 @@ static_assert(arity<>::value == 0); static_assert(arity::value == 3); ``` +An interesting use for this is creating an _initializer list_ from a _parameter pack_ in order to iterate over variadic function arguments. +```c++ +template +auto sum(const First first, const Args... args) -> decltype(first) { + const auto values = {first, args...}; + return std::accumulate(values.begin(), values.end(), First{0}); +} + +sum(1, 2, 3, 4, 5); // 15 +sum(1, 2, 3); // 6 +sum(1.5, 2.0, 3.7); // 7.2 +``` + ### Initializer lists A lightweight array-like container of elements created using a "braced list" syntax. For example, `{ 1, 2, 3 }` creates a sequences of integers, that has type `std::initializer_list`. Useful as a replacement to passing a vector of objects to a function. ```c++ @@ -644,9 +1799,9 @@ int sum(const std::initializer_list& list) { return total; } -auto list = { 1, 2, 3 }; +auto list = {1, 2, 3}; sum(list); // == 6 -sum({ 1, 2, 3 }); // == 6 +sum({1, 2, 3}); // == 6 sum({}); // == 0 ``` @@ -699,13 +1854,13 @@ A `lambda` is an unnamed function object capable of capturing variables in scope * `[]` - captures nothing. * `[=]` - capture local objects (local variables, parameters) in scope by value. * `[&]` - capture local objects (local variables, parameters) in scope by reference. -* `[this]` - capture `this` pointer by value. +* `[this]` - capture `this` by reference. * `[a, &b]` - capture objects `a` by value, `b` by reference. ```c++ int x = 1; -auto getX = [=]{ return x; }; +auto getX = [=] { return x; }; getX(); // == 1 auto addX = [=](int y) { return x + y; }; @@ -722,11 +1877,11 @@ auto f1 = [&x] { x = 2; }; // OK: x is a reference and modifies the original auto f2 = [x] { x = 2; }; // ERROR: the lambda can only perform const-operations on the captured value // vs. -auto f3 = [x] () mutable { x = 2; }; // OK: the lambda can perform any operations on the captured value +auto f3 = [x]() mutable { x = 2; }; // OK: the lambda can perform any operations on the captured value ``` ### decltype -`decltype` is an operator which returns the _declared type_ of an expression passed to it. Examples of `decltype`: +`decltype` is an operator which returns the _declared type_ of an expression passed to it. cv-qualifiers and references are maintained if they are part of the expression. Examples of `decltype`: ```c++ int a = 1; // `a` is declared as type `int` decltype(a) b = a; // `decltype(a)` is `int` @@ -745,15 +1900,17 @@ auto add(X x, Y y) -> decltype(x + y) { add(1, 2.0); // `decltype(x + y)` => `decltype(3.0)` => `double` ``` -### Template aliases -Semantically similar to using a `typedef` however, template aliases with `using` are easier to read and are compatible with templates. +See also: [`decltype(auto) (C++14)`](#decltypeauto). + +### Type aliases +Semantically similar to using a `typedef` however, type aliases with `using` are easier to read and are compatible with templates. ```c++ template using Vec = std::vector; -Vec v{}; // std::vector +Vec v; // std::vector using String = std::string; -String s{"foo"}; +String s {"foo"}; ``` ### nullptr @@ -785,7 +1942,7 @@ Attributes provide a universal syntax over `__attribute__(...)`, `__declspec`, e ``` ### constexpr -Constant expressions are expressions evaluated by the compiler at compile-time. Only non-complex computations can be carried out in a constant expression. Use the `constexpr` specifier to indicate the variable, function, etc. is a constant expression. +Constant expressions are expressions that are *possibly* evaluated by the compiler at compile-time. Only non-complex computations can be carried out in a constant expression (these rules are progressively relaxed in later versions). Use the `constexpr` specifier to indicate the variable, function, etc. is a constant expression. ```c++ constexpr int square(int x) { return x * x; @@ -801,8 +1958,9 @@ int b = square2(2); // mov edi, 2 // call square2(int) // mov DWORD PTR [rbp-8], eax ``` +In the previous snippet, notice that the computation when calling `square` is carried out at compile-time, and then the result is embedded in the code generation, while `square2` is called at run-time. -`constexpr` values are those that the compiler can evaluate at compile-time: +`constexpr` values are those that the compiler can evaluate, but are not guaranteed to, at compile-time: ```c++ const int x = 123; constexpr const int& y = x; // error -- constexpr variable `y` must be initialized by a constant expression @@ -811,7 +1969,7 @@ constexpr const int& y = x; // error -- constexpr variable `y` must be initializ Constant expressions with classes: ```c++ struct Complex { - constexpr Complex(double r, double i) : re(r), im(i) { } + constexpr Complex(double r, double i) : re{r}, im{i} { } constexpr double real() { return re; } constexpr double imag() { return im; } @@ -828,11 +1986,11 @@ Constructors can now call other constructors in the same class using an initiali ```c++ struct Foo { int foo; - Foo(int foo) : foo(foo) {} + Foo(int foo) : foo{foo} {} Foo() : Foo(0) {} }; -Foo foo{}; +Foo foo; foo.foo; // == 0 ``` @@ -891,13 +2049,8 @@ struct C : B { Class cannot be inherited from. ```c++ -struct A final { - -}; - -struct B : A { // error -- base 'A' is marked 'final' - -}; +struct A final {}; +struct B : A {}; // error -- base 'A' is marked 'final' ``` ### Default functions @@ -905,17 +2058,17 @@ A more elegant, efficient way to provide a default implementation of a function, ```c++ struct A { A() = default; - A(int x) : x(x) {} - int x{ 1 }; + A(int x) : x{x} {} + int x {1}; }; -A a{}; // a.x == 1 -A a2{ 123 }; // a.x == 123 +A a; // a.x == 1 +A a2 {123}; // a.x == 123 ``` With inheritance: ```c++ struct B { - B() : x(1); + B() : x{1} {} int x; }; @@ -924,7 +2077,7 @@ struct C : B { C() = default; }; -C c{}; // c.x == 1 +C c; // c.x == 1 ``` ### Deleted functions @@ -934,12 +2087,12 @@ class A { int x; public: - A(int x) : x(x) {}; + A(int x) : x{x} {}; A(const A&) = delete; A& operator=(const A&) = delete; }; -A x{ 123 }; +A x {123}; A y = x; // error -- call to deleted copy constructor y = x; // error -- operator= deleted ``` @@ -947,14 +2100,14 @@ y = x; // error -- operator= deleted ### Range-based for loops Syntactic sugar for iterating over a container's elements. ```c++ -std::array a{ 1, 2, 3, 4, 5 }; +std::array a {1, 2, 3, 4, 5}; for (int& x : a) x *= 2; // a == { 2, 4, 6, 8, 10 } ``` Note the difference when using `int` as opposed to `int&`: ```c++ -std::array a{ 1, 2, 3, 4, 5 }; +std::array a {1, 2, 3, 4, 5}; for (int x : a) x *= 2; // a == { 1, 2, 3, 4, 5 } ``` @@ -964,9 +2117,9 @@ The copy constructor and copy assignment operator are called when copies are mad ```c++ struct A { std::string s; - A() : s("test") {} - A(const A& o) : s(o.s) {} - A(A&& o) : s(std::move(o.s)) {} + A() : s{"test"} {} + A(const A& o) : s{o.s} {} + A(A&& o) : s{std::move(o.s)} {} A& operator=(A&& o) { s = std::move(o.s); return *this; @@ -993,10 +2146,10 @@ struct A { A(int, int, int) {} }; -A a{0, 0}; // calls A::A(int, int) +A a {0, 0}; // calls A::A(int, int) A b(0, 0); // calls A::A(int, int) A c = {0, 0}; // calls A::A(int, int) -A d{0, 0, 0}; // calls A::A(int, int, int) +A d {0, 0, 0}; // calls A::A(int, int, int) ``` Note that the braced list syntax does not allow narrowing: @@ -1006,7 +2159,7 @@ struct A { }; A a(1.1); // OK -A b{1.1}; // Error narrowing conversion from double to int +A b {1.1}; // Error narrowing conversion from double to int ``` Note that if a constructor accepts a `std::initializer_list`, it will be called instead: @@ -1018,10 +2171,10 @@ struct A { A(std::initializer_list) {} }; -A a{0, 0}; // calls A::A(std::initializer_list) +A a {0, 0}; // calls A::A(std::initializer_list) A b(0, 0); // calls A::A(int, int) A c = {0, 0}; // calls A::A(std::initializer_list) -A d{0, 0, 0}; // calls A::A(std::initializer_list) +A d {0, 0, 0}; // calls A::A(std::initializer_list) ``` ### Explicit conversion functions @@ -1035,11 +2188,11 @@ struct B { explicit operator bool() const { return true; } }; -A a{}; +A a; if (a); // OK calls A::operator bool() bool ba = a; // OK copy-initialization selects A::operator bool() -B b{}; +B b; if (b); // OK calls B::operator bool() bool bb = b; // error copy-initialization does not consider B::operator bool() ``` @@ -1068,19 +2221,18 @@ Allows non-static data members to be initialized where they are declared, potent ```c++ // Default initialization prior to C++11 class Human { - Human() : age(0) {} + Human() : age{0} {} private: unsigned age; }; // Default initialization on C++11 class Human { private: - unsigned age{0}; + unsigned age {0}; }; ``` - -### Right angle Brackets +### Right angle brackets C++11 is now able to infer when a series of right angle brackets is used as an operator or as a closing statement of typedef, without having to add whitespace. ```c++ @@ -1088,12 +2240,121 @@ typedef std::map > > cpp98LongTypedef; typedef std::map>> cpp11LongTypedef; ``` +### Ref-qualified member functions +Member functions can now be qualified depending on whether `*this` is an lvalue or rvalue reference. + +```c++ +struct Bar { + // ... +}; + +struct Foo { + Bar& getBar() & { return bar; } + const Bar& getBar() const& { return bar; } + Bar&& getBar() && { return std::move(bar); } + const Bar&& getBar() const&& { return std::move(bar); } +private: + Bar bar; +}; + +Foo foo{}; +Bar bar = foo.getBar(); // calls `Bar& getBar() &` + +const Foo foo2{}; +Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&` + +Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&` +std::move(foo).getBar(); // calls `Bar&& Foo::getBar() &&` +std::move(foo2).getBar(); // calls `const Bar&& Foo::getBar() const&` +``` + +### Trailing return types +C++11 allows functions and lambdas an alternative syntax for specifying their return types. +```c++ +int f() { + return 123; +} +// vs. +auto f() -> int { + return 123; +} +``` +```c++ +auto g = []() -> int { + return 123; +}; +``` +This feature is especially useful when certain return types cannot be resolved: +```c++ +// NOTE: This does not compile! +template +decltype(a + b) add(T a, U b) { + return a + b; +} + +// Trailing return types allows this: +template +auto add(T a, U b) -> decltype(a + b) { + return a + b; +} +``` +In C++14, [`decltype(auto) (C++14)`](#decltypeauto) can be used instead. + +### Noexcept specifier +The `noexcept` specifier specifies whether a function could throw exceptions. It is an improved version of `throw()`. + +```c++ +void func1() noexcept; // does not throw +void func2() noexcept(true); // does not throw +void func3() throw(); // does not throw + +void func4() noexcept(false); // may throw +``` + +Non-throwing functions are permitted to call potentially-throwing functions. Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function std::terminate is called. + +```c++ +extern void f(); // potentially-throwing +void g() noexcept { + f(); // valid, even if f throws + throw 42; // valid, effectively a call to std::terminate +} +``` + +### char32_t and char16_t +Provides standard types for representing UTF-8 strings. +```c++ +char32_t utf8_str[] = U"\u0123"; +char16_t utf8_str[] = u"\u0123"; +``` + +### Raw string literals +C++11 introduces a new way to declare string literals as "raw string literals". Characters issued from an escape sequence (tabs, line feeds, single backslashes, etc.) can be inputted raw while preserving formatting. This is useful, for example, to write literary text, which might contain a lot of quotes or special formatting. This can make your string literals easier to read and maintain. + +A raw string literal is declared using the following syntax: +``` +R"delimiter(raw_characters)delimiter" +``` +where: +* `delimiter` is an optional sequence of characters made of any source character except parentheses, backslashes and spaces. +* `raw_characters` is any raw character sequence; must not contain the closing sequence `")delimiter"`. + +Example: +```cpp +// msg1 and msg2 are equivalent. +const char* msg1 = "\nHello,\n\tworld!\n"; +const char* msg2 = R"( +Hello, + world! +)"; +``` + ## C++11 Library Features ### std::move -`std::move` indicates that the object passed to it may be moved, or in other words, moved from one object to another without a copy. The object passed in should not be used after the move in certain situations. +`std::move` indicates that the object passed to it may have its resources transferred. Using objects that have been moved from should be used with care, as they can be left in an unspecified state (see: [What can I do with a moved-from object?](http://stackoverflow.com/questions/7027523/what-can-i-do-with-a-moved-from-object)). -A definition of `std::move` (performing a move is nothing more than casting to an rvalue): +A definition of `std::move` (performing a move is nothing more than casting to an rvalue reference): ```c++ template typename remove_reference::type&& move(T&& arg) { @@ -1103,18 +2364,14 @@ typename remove_reference::type&& move(T&& arg) { Transferring `std::unique_ptr`s: ```c++ -std::unique_ptr p1{ new int }; +std::unique_ptr p1 {new int{0}}; // in practice, use std::make_unique std::unique_ptr p2 = p1; // error -- cannot copy unique pointers -std::unique_ptr p3 = std::move(p1); // move `p1` into `p2` +std::unique_ptr p3 = std::move(p1); // move `p1` into `p3` // now unsafe to dereference object held by `p1` ``` ### std::forward -Returns the arguments passed to it as-is, either as an lvalue or rvalue references, and includes cv-qualification. Useful for generic code that need a reference (either lvalue or rvalue) when appropriate, e.g factories. Forwarding gets its power from _template argument deduction_: -* `T& &` becomes `T&` -* `T& &&` becomes `T&` -* `T&& &` becomes `T&` -* `T&& &&` becomes `T&&` +Returns the arguments passed to it while maintaining their value category and cv-qualifiers. Useful for generic code and factories. Used in conjunction with [`forwarding references`](#forwarding-references). A definition of `std::forward`: ```c++ @@ -1134,15 +2391,33 @@ struct A { template A wrapper(T&& arg) { - return A{ std::forward(arg) }; + return A{std::forward(arg)}; } wrapper(A{}); // moved -A a{}; +A a; wrapper(a); // copied wrapper(std::move(a)); // moved ``` +See also: [`forwarding references`](#forwarding-references), [`rvalue references`](#rvalue-references). + +### std::thread +The `std::thread` library provides a standard way to control threads, such as spawning and killing them. In the example below, multiple threads are spawned to do different calculations and then the program waits for all of them to finish. + +```c++ +void foo(bool clause) { /* do something... */ } + +std::vector threadsVector; +threadsVector.emplace_back([]() { + // Lambda function that will be invoked +}); +threadsVector.emplace_back(foo, true); // thread will run foo(true) +for (auto& thread : threadsVector) { + thread.join(); // Wait for threads to finish +} +``` + ### std::to_string Converts a numeric argument to a `std::string`. ```c++ @@ -1153,27 +2428,31 @@ std::to_string(123); // == "123" ### Type traits Type traits defines a compile-time template-based interface to query or modify the properties of types. ```c++ -static_assert(std::is_integral::value == 1); -static_assert(std::is_same::value == 1); -static_assert(std::is_same::type, int>::value == 1); +static_assert(std::is_integral::value); +static_assert(std::is_same::value); +static_assert(std::is_same::type, int>::value); ``` ### Smart pointers -C++11 introduces new smart(er) pointers: `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr`. `std::auto_ptr` now becomes deprecated and then eventually removed in C++17. +C++11 introduces new smart pointers: `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr`. `std::auto_ptr` now becomes deprecated and then eventually removed in C++17. -`std::unique_ptr` is a non-copyable, movable smart pointer that properly manages arrays and STL containers. **Note: Prefer using the `std::make_X` helper functions as opposed to using constructors. See the sections for [std::make_unique](#stdmake_unique) and [std::make_shared](#stdmake_shared).** +`std::unique_ptr` is a non-copyable, movable pointer that manages its own heap-allocated memory. **Note: Prefer using the `std::make_X` helper functions as opposed to using constructors. See the sections for [std::make_unique](https://github.com/AnthonyCalandra/modern-cpp-features/blob/master/CPP14.md#stdmake_unique) and [std::make_shared](#stdmake_shared).** ```c++ std::unique_ptr p1 { new Foo{} }; // `p1` owns `Foo` -if (p1) p1->bar(); +if (p1) { + p1->bar(); +} { - std::unique_ptr p2 { std::move(p1) }; // Now `p2` owns `Foo` + std::unique_ptr p2 {std::move(p1)}; // Now `p2` owns `Foo` f(*p2); p1 = std::move(p2); // Ownership returns to `p1` -- `p2` gets destroyed } -if (p1) p1->bar(); +if (p1) { + p1->bar(); +} // `Foo` instance is destroyed when `p1` goes out of scope ``` @@ -1191,7 +2470,7 @@ void baz(std::shared_ptr t) { // Do something with `t`... } -std::shared_ptr p1 { new T{} }; +std::shared_ptr p1 {new T{}}; // Perhaps these take place in another threads? foo(p1); bar(p1); @@ -1201,20 +2480,19 @@ baz(p1); ### std::chrono The chrono library contains a set of utility functions and types that deal with _durations_, _clocks_, and _time points_. One use case of this library is benchmarking code: ```c++ -std::chrono::time_point start, end; -start = std::chrono::system_clock::now(); +std::chrono::time_point start, end; +start = std::chrono::steady_clock::now(); // Some computations... -end = std::chrono::system_clock::now(); +end = std::chrono::steady_clock::now(); -std::chrono::duration elapsed_seconds = end-start; - -elapsed_seconds.count(); // t number of seconds, represented as a `double` +std::chrono::duration elapsed_seconds = end - start; +double t = elapsed_seconds.count(); // t number of seconds, represented as a `double` ``` ### Tuples Tuples are a fixed-size collection of heterogeneous values. Access the elements of a `std::tuple` by unpacking using [`std::tie`](#stdtie), or using `std::get`. ```c++ -// `playerProfile` has type `std::tuple`. +// `playerProfile` has type `std::tuple`. auto playerProfile = std::make_tuple(51, "Frans Nielsen", "NYI"); std::get<0>(playerProfile); // 51 std::get<1>(playerProfile); // "Frans Nielsen" @@ -1254,7 +2532,7 @@ These containers maintain average constant-time complexity for search, insert, a * Prevents code repetition when specifying the underlying type the pointer shall hold. * It provides exception-safety. Suppose we were calling a function `foo` like so: ```c++ -foo(std::shared_ptr{ new T{} }, function_that_throws(), std::shared_ptr{ new T{} }); +foo(std::shared_ptr{new T{}}, function_that_throws(), std::shared_ptr{new T{}}); ``` The compiler is free to call `new T{}`, then `function_that_throws()`, and so on... Since we have allocated data on the heap in the first construction of a `T`, we have introduced a leak here. With `std::make_shared`, we are given exception-safety: ```c++ @@ -1264,19 +2542,74 @@ foo(std::make_shared(), function_that_throws(), std::make_shared()); See the section on [smart pointers](#smart-pointers) for more information on `std::unique_ptr` and `std::shared_ptr`. +### std::ref +`std::ref(val)` is used to create object of type `std::reference_wrapper` that holds reference of val. Used in cases when usual reference passing using `&` does not compile or `&` is dropped due to type deduction. `std::cref` is similar but created reference wrapper holds a const reference to val. + +```c++ +// create a container to store reference of objects. +auto val = 99; +auto _ref = std::ref(val); +_ref++; +auto _cref = std::cref(val); +//_cref++; does not compile +std::vector>vec; // vectorvec does not compile +vec.push_back(_ref); // vec.push_back(&i) does not compile +cout << val << endl; // prints 100 +cout << vec[0] << endl; // prints 100 +cout << _cref; // prints 100 +``` + ### Memory model C++11 introduces a memory model for C++, which means library support for threading and atomic operations. Some of these operations include (but aren't limited to) atomic loads/stores, compare-and-swap, atomic flags, promises, futures, locks, and condition variables. +See the sections on: [std::thread](#stdthread) + +### std::async +`std::async` runs the given function either asynchronously or lazily-evaluated, then returns a `std::future` which holds the result of that function call. + +The first parameter is the policy which can be: +1. `std::launch::async | std::launch::deferred` It is up to the implementation whether to perform asynchronous execution or lazy evaluation. +1. `std::launch::async` Run the callable object on a new thread. +1. `std::launch::deferred` Perform lazy evaluation on the current thread. + +```c++ +int foo() { + /* Do something here, then return the result. */ + return 1000; +} + +auto handle = std::async(std::launch::async, foo); // create an async task +auto result = handle.get(); // wait for the result +``` + +### std::begin/end +`std::begin` and `std::end` free functions were added to return begin and end iterators of a container generically. These functions also work with raw arrays which do not have `begin` and `end` member functions. + +```c++ +template +int CountTwos(const T& container) { + return std::count_if(std::begin(container), std::end(container), [](int item) { + return item == 2; + }); +} + +std::vector vec = {2, 2, 43, 435, 4543, 534}; +int arr[8] = {2, 43, 45, 435, 32, 32, 32, 32}; +auto a = CountTwos(vec); // 2 +auto b = CountTwos(arr); // 1 +``` + + + ## 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. +* [C++ Rvalue References Explained](http://web.archive.org/web/20240324121501/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! +* [Scott Meyers' Effective Modern C++](https://www.amazon.com/Effective-Modern-Specific-Ways-Improve/dp/1491903996) - highly recommended series of books! * [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