# C++17
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
Many of these descriptions and examples are taken from various resources (see Acknowledgements section) and summarized in my own words.
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++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
.
|
|
# 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 book!
- 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)?
- 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