# Modern C++ Features (Anthony Calandra), overview C++20/17/14/11
NOTE: this is a web “mirror” of Anthony Calandra’s modern-cpp-features shared under MIT License (see at bottom). The only reason I do a copy is I hate reading markdowns from github. I want something simple and plain for my own reference.
# Overview
C++20 includes the following new language features:
- coroutines
- concepts
- three-way comparison
- designated initializers
- template syntax for lambdas
- range-based for loop with initializer
[[likely]]
and[[unlikely]]
attributes- deprecate implicit capture of this
- class types in non-type template parameters
- constexpr virtual functions
- explicit(bool)
- immediate functions
- using enum
- lambda capture of parameter pack
- char8_t
- constinit
- __VA_OPT__
C++20 includes the following new library features:
- concepts library
- formatting library
- synchronized buffered outputstream
- std::span
- bit operations
- math constants
- std::is_constant_evaluated
- std::make_shared supports arrays
- starts_with and ends_with on strings
- check if associative container has element
- std::bit_cast
- std::midpoint
- std::to_array
- std::bind_front
- uniform container erasure
- three-way comparison helpers
- std::lexicographical_compare_three_way
C++17 includes the following new language features:
- template argument deduction for class templates
- declaring non-type template parameters with auto
- folding expressions
- new rules for auto deduction from braced-init-list
- constexpr lambda
- lambda capture this by value
- inline variables
- nested namespaces
- structured bindings
- selection statements with initializer
- constexpr if
- utf-8 character literals
- direct-list-initialization of enums
[[fallthrough]]
,[[nodiscard]]
,[[maybe_unused]]
attributes- __has_include
- class template argument deduction
C++17 includes the following new library features:
- std::variant
- std::optional
- std::any
- std::string_view
- std::invoke
- std::apply
- std::filesystem
- std::byte
- splicing for maps and sets
- parallel algorithms
- std::sample
- std::clamp
- std::reduce
- prefix sum algorithms
- gcd and lcm
- std::not_fn
- string conversion to/from numbers
- rounding functions for chrono durations and timepoints
C++14 includes the following new language features:
- binary literals
- generic lambda expressions
- lambda capture initializers
- return type deduction
- decltype(auto)
- relaxing constraints on constexpr functions
- variable templates
[[deprecated]]
attribute
C++14 includes the following new library features:
C++11 includes the following new language features:
- move semantics
- variadic templates
- rvalue references
- forwarding references
- initializer lists
- static assertions
- auto
- lambda expressions
- decltype
- type aliases
- nullptr
- strongly-typed enums
- attributes
- constexpr
- delegating constructors
- user-defined literals
- explicit virtual overrides
- final specifier
- default functions
- deleted functions
- range-based for loops
- special member functions for move semantics
- converting constructors
- explicit conversion functions
- inline-namespaces
- non-static data member initializers
- right angle brackets
- ref-qualified member functions
- trailing return types
- noexcept specifier
- char32_t and char16_t
- raw string literals
C++11 includes the following new library features:
- std::move
- std::forward
- std::thread
- std::to_string
- type traits
- smart pointers
- std::chrono
- tuples
- std::tie
- std::array
- unordered containers
- std::make_shared
- std::ref
- memory model
- std::async
- std::begin/end
# 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
andtask
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:
|
|
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:
|
|
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:
|
|
# 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.
|
|
There are a variety of syntactic forms for enforcing concepts:
|
|
The requires
keyword is used either to start a requires
clause or a requires
expression:
|
|
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.
|
|
- Type requirements - denoted by the
typename
keyword followed by a type name, asserts that the given type name is valid.
|
|
- Compound requirements - an expression in braces followed by a trailing return type or type constraint.
|
|
- Nested requirements - denoted by the
requires
keyword, specify additional constraints (such as those on local parameter arguments).
|
|
See also: 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). Providesless
,greater
,equivalent
, andequal
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). Providesless
,greater
, andequivalent
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. Providesless
,greater
,equivalent
, andunordered
ordering. Examples of comparisons: floating-point values (e.g.NaN
).
A defaulted three-way comparison operator does a member-wise comparison:
|
|
You can also define your own comparisons:
|
|
# Designated initializers
C-style designated initializer syntax. Any member fields that are not explicitly listed in the designated initializer list are default-initialized.
|
|
# Template syntax for lambdas
Use familiar template syntax in lambda expressions.
|
|
# 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.
|
|
#
[[likely]]
and [[unlikely]]
attributes
Provides a hint to the optimizer that the labelled statement has a high probability of being executed.
|
|
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.
|
|
It can also be applied to the substatement (body) of an iteration statement.
|
|
# Deprecate implicit capture of this
Implicitly capturing this
in a lambda capture using [=]
is now deprecated; prefer capturing explicitly using [=, this]
or [=, *this]
.
|
|
# 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.
|
|
# 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.
|
|
# explicit(bool)
Conditionally select at compile-time whether a constructor is made explicit or not. explicit(true)
is the same as specifying explicit
.
|
|
# Immediate functions
Similar to constexpr
functions, but functions with a consteval
specifier must produce a constant. These are called immediate functions
.
|
|
# using enum
Bring an enum’s members into scope to improve readability. Before:
|
|
After:
|
|
# Lambda capture of parameter pack
Capture parameter packs by value:
|
|
Capture parameter packs by reference:
|
|
# char8_t
Provides a standard type for representing UTF-8 strings.
|
|
# constinit
The constinit
specifier requires that a variable must be initialized at compile-time.
|
|
# __VA_OPT__
Helps support variadic macros by evaluating to the given argument if the variadic macro is non-empty.
|
|
# 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 thatoperator==
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 bothsemiregular
andequality_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.
# 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.
|
|
To format custom types:
|
|
# Synchronized buffered outputstream
Buffers output operations for the wrapped output stream ensuring synchronization (i.e. no interleaving of output).
|
|
# 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<const T>
.
Example: using a dynamically-sized span to print integers from various containers.
|
|
Example: a statically-sized span will fail to compile for containers that don’t match the extent of the span.
|
|
# Bit operations
C++20 provides a new <bit>
header which provides some bit operations including popcount.
|
|
# Math constants
Mathematical constants including PI, Euler’s number, etc. defined in the <numbers>
header.
|
|
# std::is_constant_evaluated
Predicate function which is truthy when it is called in a compile-time context.
|
|
# std::make_shared supports arrays
|
|
# 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.
|
|
# 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.
|
|
# std::bit_cast
A safer way to reinterpret an object from one type to another.
|
|
# std::midpoint
Calculate the midpoint of two integers safely (without overflow).
|
|
# std::to_array
Converts the given array/“array-like” object to a 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.
|
|
# 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.
|
|
# Three-way comparison helpers
Helper functions for giving names to comparison results:
|
|
See also: 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.
|
|
See also: three-way comparison, three-way comparison helpers.
# C++17 Language Features
# Template argument deduction for class templates
Automatic template argument deduction much like how it’s done for functions, but now including class constructors.
|
|
# 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:
|
|
* - For example, you cannot use a double
as a template parameter type, which also makes this an invalid deduction using auto
.
# Folding expressions
A fold expression performs a fold of a template parameter pack over a binary operator.
- An expression of the form
(... op e)
or(e op ...)
, whereop
is a fold-operator ande
is an unexpanded parameter pack, are called unary folds. - An expression of the form
(e1 op ... op e2)
, whereop
are fold-operators, is called a binary fold. Eithere1
ore2
is an unexpanded parameter pack, but not both.
|
|
|
|
# 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<int>
, which now deduces to int
.
|
|
# constexpr lambda
Compile-time lambdas using constexpr
.
|
|
|
|
|
|
#
Lambda capture this
by value
Capturing this
in a lambda’s environment was previously reference-only. An example of where this is problematic is asynchronous code using callbacks that require an object to be available, potentially past its lifetime. *this
(C++17) will now make a copy of the current object, while this
(C++11) continues to capture by reference.
|
|
# Inline variables
The inline specifier can be applied to variables as well as to functions. A variable declared inline has the same semantics as a function declared inline.
|
|
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.
|
|
# Nested namespaces
Using the namespace resolution operator to create nested namespace definitions.
|
|
The code above can be written like this:
|
|
# 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.
|
|
|
|
# Selection statements with initializer
New versions of the if
and switch
statements which simplify common code patterns and help users keep scopes tight.
|
|
|
|
# constexpr if
Write code that is instantiated depending on a compile-time condition.
|
|
# 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.
|
|
# Direct list initialization of enums
Enums can now be initialized using braced syntax.
|
|
#
[[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.
|
|
[[nodiscard]]
issues a warning when either a function or class has this attribute and its return value is discarded.
|
|
|
|
[[maybe_unused]]
indicates to the compiler that a variable or parameter might be unused and is intended.
|
|
# __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.
|
|
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.
|
|
# Class template argument deduction
Class template argument deduction (CTAD) allows the compiler to deduce template arguments from constructor arguments.
|
|
For user-defined types, deduction guides can be used to guide the compiler how to deduce template arguments if applicable:
|
|
# C++17 Library Features
# std::variant
The class template std::variant
represents a type-safe union
. An instance of std::variant
at any given time holds a value of one of its alternative types (it’s also possible for it to be valueless).
|
|
# std::optional
The class template std::optional
manages an optional contained value, i.e. a value that may or may not be present. A common use case for optional is the return value of a function that may fail.
|
|
# std::any
A type-safe container for single values of any type.
|
|
# std::string_view
A non-owning reference to a string. Useful for providing an abstraction on top of strings (e.g. for parsing).
|
|
|
|
# std::invoke
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.
|
|
# std::apply
Invoke a Callable
object with a tuple of arguments.
|
|
# 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:
|
|
# 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.
|
|
Note that std::byte
is simply an enum, and braced initialization of enums become possible thanks to 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:
|
|
Inserting an entire set:
|
|
Inserting elements which outlive the container:
|
|
Changing the key of a map element:
|
|
# 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”.
|
|
# std::sample
Samples n elements in the given sequence (without replacement) where every element has an equal chance of being selected.
|
|
# std::clamp
Clamp given value between a lower and upper bound.
|
|
# 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.
|
|
Additionally you can specify transformations for reducers:
|
|
# Prefix sum algorithms
Support for prefix sums (both inclusive and exclusive scans) along with transformations.
|
|
# GCD and LCM
Greatest common divisor (GCD) and least common multiple (LCM).
|
|
# std::not_fn
Utility function that returns the negation of the result of the given function.
|
|
# 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
:
|
|
Convert from a std::string
with value "123"
to an integer:
|
|
# 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++14 Language Features
# Binary literals
Binary literals provide a convenient way to represent a base-2 number.
It is possible to separate digits with '
.
|
|
# Generic lambda expressions
C++14 now allows the auto
type-specifier in the parameter list, enabling polymorphic lambdas.
|
|
# Lambda capture initializers
This allows creating lambda captures initialized with arbitrary expressions. The name given to the captured value does not need to be related to any variables in the enclosing scopes and introduces a new name inside the lambda body. The initializing expression is evaluated when the lambda is created (not when it is invoked).
|
|
Because it is now possible to move (or forward) values into a lambda that could previously be only captured by copy or reference we can now capture move-only types in a lambda by value. Note that in the below example the p
in the capture-list of task2
on the left-hand-side of =
is a new variable private to the lambda body and does not refer to the original p
.
|
|
Using this reference-captures can have different names than the referenced variable.
|
|
# Return type deduction
Using an auto
return type in C++14, the compiler will attempt to deduce the type for you. With lambdas, you can now deduce its return type using auto
, which makes returning a deduced reference or rvalue reference possible.
|
|
|
|
# decltype(auto)
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.
|
|
|
|
See also: decltype (C++11)
.
# 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.
|
|
# Variable templates
C++14 allows variables to be templated:
|
|
#
[[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++14 Library Features
# User-defined literals for standard library types
New user-defined literals for standard library types, including new built-in literals for chrono
and basic_string
. These can be constexpr
meaning they can be used at compile-time. Some uses for these literals include compile-time integer parsing, binary literals, and imaginary number literals.
|
|
# 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<T, N>
- creates a sequence of0, ..., N - 1
with typeT
.std::index_sequence_for<T...>
- converts a template parameter pack into an integer sequence.
Convert an array into a tuple:
|
|
# std::make_unique
std::make_unique
is the recommended way to create instances of std::unique_ptr
s due to the following reasons:
- Avoid having to use the
new
operator. - 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:
|
|
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:
|
|
See the section on smart pointers (C++11) for more information on std::unique_ptr
and std::shared_ptr
.
# C++11 Language Features
# Move semantics
Moving an object means to transfer ownership of some resource it manages to another object.
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 for non-copyable types such as std::unique_ptr
s (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, special member functions for move semantics, std::move
, std::forward
, forwarding references
.
# Rvalue references
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.
Type deduction with lvalues and rvalues:
|
|
See also: std::move
, std::forward
, 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& &
becomesT&
T& &&
becomesT&
T&& &
becomesT&
T&& &&
becomesT&&
auto
type deduction with lvalues and rvalues:
|
|
Template type parameter deduction with lvalues and rvalues:
|
|
See also: std::move
, std::forward
, 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.
|
|
An interesting use for this is creating an initializer list from a parameter pack in order to iterate over variadic function arguments.
|
|
# 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<int>
. Useful as a replacement to passing a vector of objects to a function.
|
|
# Static assertions
Assertions that are evaluated at compile-time.
|
|
# auto
auto
-typed variables are deduced by the compiler according to the type of their initializer.
|
|
Extremely useful for readability, especially for complicated types:
|
|
Functions can also deduce the return type using auto
. In C++11, a return type must be specified either explicitly, or using decltype
like so:
|
|
The trailing return type in the above example is the declared type (see section on decltype
) of the expression x + y
. For example, if x
is an integer and y
is a double, decltype(x + y)
is a double. Therefore, the above function will deduce the type depending on what type the expression x + y
yields. Notice that the trailing return type has access to its parameters, and this
when appropriate.
# Lambda expressions
A lambda
is an unnamed function object capable of capturing variables in scope. It features: a capture list; an optional set of parameters with an optional trailing return type; and a body. Examples of capture lists:
[]
- captures nothing.[=]
- capture local objects (local variables, parameters) in scope by value.[&]
- capture local objects (local variables, parameters) in scope by reference.[this]
- capturethis
by reference.[a, &b]
- capture objectsa
by value,b
by reference.
|
|
By default, value-captures cannot be modified inside the lambda because the compiler-generated method is marked as const
. The mutable
keyword allows modifying captured variables. The keyword is placed after the parameter-list (which must be present even if it is empty).
|
|
# 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
:
|
|
|
|
See also: decltype(auto) (C++14)
.
# Type aliases
Semantically similar to using a typedef
however, type aliases with using
are easier to read and are compatible with templates.
|
|
# nullptr
C++11 introduces a new null pointer type designed to replace C’s NULL
macro. nullptr
itself is of type std::nullptr_t
and can be implicitly converted into pointer types, and unlike NULL
, not convertible to integral types except bool
.
|
|
# Strongly-typed enums
Type-safe enums that solve a variety of problems with C-style enums including: implicit conversions, inability to specify the underlying type, scope pollution.
|
|
# Attributes
Attributes provide a universal syntax over __attribute__(...)
, __declspec
, etc.
|
|
# constexpr
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.
|
|
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, but are not guaranteed to, at compile-time:
|
|
Constant expressions with classes:
|
|
# Delegating constructors
Constructors can now call other constructors in the same class using an initializer list.
|
|
# User-defined literals
User-defined literals allow you to extend the language and add your own syntax. To create a literal, define a T operator "" X(...) { ... }
function that returns a type T
, with a name X
. Note that the name of this function defines the name of the literal. Any literal names not starting with an underscore are reserved and won’t be invoked. There are rules on what parameters a user-defined literal function should accept, according to what type the literal is called on.
Converting Celsius to Fahrenheit:
|
|
String to integer conversion:
|
|
# Explicit virtual overrides
Specifies that a virtual function overrides another virtual function. If the virtual function does not override a parent’s virtual function, throws a compiler error.
|
|
# Final specifier
Specifies that a virtual function cannot be overridden in a derived class or that a class cannot be inherited from.
|
|
Class cannot be inherited from.
|
|
# Default functions
A more elegant, efficient way to provide a default implementation of a function, such as a constructor.
|
|
With inheritance:
|
|
# Deleted functions
A more elegant, efficient way to provide a deleted implementation of a function. Useful for preventing copies on objects.
|
|
# Range-based for loops
Syntactic sugar for iterating over a container’s elements.
|
|
Note the difference when using int
as opposed to int&
:
|
|
# Special member functions for move semantics
The copy constructor and copy assignment operator are called when copies are made, and with C++11’s introduction of move semantics, there is now a move constructor and move assignment operator for moves.
|
|
# Converting constructors
Converting constructors will convert values of braced list syntax into constructor arguments.
|
|
Note that the braced list syntax does not allow narrowing:
|
|
Note that if a constructor accepts a std::initializer_list
, it will be called instead:
|
|
# Explicit conversion functions
Conversion functions can now be made explicit using the explicit
specifier.
|
|
# Inline namespaces
All members of an inline namespace are treated as if they were part of its parent namespace, allowing specialization of functions and easing the process of versioning. This is a transitive property, if A contains B, which in turn contains C and both B and C are inline namespaces, C’s members can be used as if they were on A.
|
|
# Non-static data member initializers
Allows non-static data members to be initialized where they are declared, potentially cleaning up constructors of default initializations.
|
|
# 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.
|
|
# Ref-qualified member functions
Member functions can now be qualified depending on whether *this
is an lvalue or rvalue reference.
|
|
# Trailing return types
C++11 allows functions and lambdas an alternative syntax for specifying their return types.
|
|
|
|
This feature is especially useful when certain return types cannot be resolved:
|
|
In C++14, decltype(auto) (C++14)
can be used instead.
# Noexcept specifier
The noexcept
specifier specifies whether a function could throw exceptions. It is an improved version of 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.
|
|
# char32_t and char16_t
Provides standard types for representing UTF-8 strings.
|
|
# 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:
|
|
# C++11 Library Features
# std::move
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?).
A definition of std::move
(performing a move is nothing more than casting to an rvalue reference):
|
|
Transferring std::unique_ptr
s:
|
|
# std::forward
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
.
A definition of std::forward
:
|
|
An example of a function wrapper
which just forwards other A
objects to a new A
object’s copy or move constructor:
|
|
See also: forwarding 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.
|
|
# std::to_string
Converts a numeric argument to a std::string
.
|
|
# Type traits
Type traits defines a compile-time template-based interface to query or modify the properties of types.
|
|
# Smart pointers
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 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 and std::make_shared.
|
|
A std::shared_ptr
is a smart pointer that manages a resource that is shared across multiple owners. A shared pointer holds a control block which has a few components such as the managed object and a reference counter. All control block access is thread-safe, however, manipulating the managed object itself is not thread-safe.
|
|
# 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:
|
|
# Tuples
Tuples are a fixed-size collection of heterogeneous values. Access the elements of a std::tuple
by unpacking using std::tie
, or using std::get
.
|
|
# std::tie
Creates a tuple of lvalue references. Useful for unpacking std::pair
and std::tuple
objects. Use std::ignore
as a placeholder for ignored values. In C++17, structured bindings should be used instead.
|
|
# std::array
std::array
is a container built on top of a C-style array. Supports common container operations such as sorting.
|
|
# Unordered containers
These containers maintain average constant-time complexity for search, insert, and remove operations. In order to achieve constant-time complexity, sacrifices order for speed by hashing elements into buckets. There are four unordered containers:
unordered_set
unordered_multiset
unordered_map
unordered_multimap
# std::make_shared
std::make_shared
is the recommended way to create instances of std::shared_ptr
s due to the following reasons:
- Avoid having to use the
new
operator. - 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:
|
|
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:
|
|
- Prevents having to do two allocations. When calling
std::shared_ptr{ new T{} }
, we have to allocate memory forT
, then in the shared pointer we have to allocate memory for the control block within the pointer.
See the section on 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.
|
|
# 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
# 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:
std::launch::async | std::launch::deferred
It is up to the implementation whether to perform asynchronous execution or lazy evaluation.std::launch::async
Run the callable object on a new thread.std::launch::deferred
Perform lazy evaluation on the current thread.
|
|
# 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.
|
|
# Acknowledgements
- cppreference - especially useful for finding examples and documentation of new library features.
- C++ Rvalue References Explained - a great introduction I used to understand rvalue references, perfect forwarding, and move semantics.
- clang and gcc’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
- Scott Meyers’ Effective Modern C++ - highly recommended series of books!
- Jason Turner’s C++ Weekly - nice collection of C++-related videos.
- What can I do with a moved-from object?
- What are some uses of decltype(auto)?
# COPYING & ATTRIBUTIONS:
Author Anthony Calandra
Content Contributors
See: https://github.com/AnthonyCalandra/modern-cpp-features/graphs/contributors
License: The MIT License (MIT) 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:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.