Modern C++
Modern C++ = at least C++11 (i.e. C++11, C++14, C++17, C++20, C++23, C++26).
- C++23: incremental improvements.
- C++20 big new features: modules, ranges, coroutines, concepts
- C++17: added and removed many features, but not as big as C++11 or C++20.
- C++14: a small extension over C++11, with bug fixes and small improvements.
- C++11 big new features: smart pointers, lambda expressions, rvalue references
Check if a feature is supported in clang: https://clang.llvm.org/cxx_status.html
Smart Pointers
- Read more: Smart Pointer
noexcept
Compile-time check, specifies that the function will NOT throw any exceptions.
- Read more: C++ Keywords - noexcept
Type inference: auto
- Read more: C++ Keywords - auto
Range-based for loop
Similar to Java and Python:
for (int& x : my_array)
x *= 2;
Lambda Function
[](int x, int y) -> int { return x + y; }
Polymorphic lambda: in the following example, type of x
depends on type of n
double n = 1.0; // or int n = 1;
auto fp = [] (const auto &n) -> auto {return n*2;};
auto x = fp(n);
Function Object (Functors)
An object that has an operator()
defined; it can be called with function call syntax.
std::function
(since C++11) requires that the callable it wraps be CopyConstructiblestd::move_only_function
(since C++23) can store and invoke any constructible (not required to be move constructible)
At its core, std::function
is a type-erasing container, which may be used to hold any functor.
Functions are value types; no heap allocation is required.
RAII: Resource Acquisition Is Initialization
A common C++ pattern. It couples the use of a resource to the lifetime of an object:
- construction of the object <=> acquire the resource
- destruction of the object <=> release the resource.
This is similar to try-with-resources in Java
C++14 type traits
std::is_signed<T>::value; /* ---> */ std::is_signed_v<T>;
std::remove_const<T>::type; /* ---> */ std::remove_const_t<T>;
Modules
Since C++20.
A language feature allowing for a principled specification of component boundaries, rather than using textual inclusion.
Traditionally, when a C++ library X wants to expose its interface to another library Y, X provides a set of declarations in the form of a .h
file, which is textually copied into Y’s source files by the preprocessor.
problems:
- The compiler cannot restrict visibility of symbols.
- Textual inclusion is brittle. the point and order of inclusion affects the meaning of the program.
- Textual inclusion does not scale. without ongoing cleanup efforts, textual inclusion leads to a superlinear growth in average C++ parse times.
Modules:
- Will replace header files in the long run;
- explicit about what is exported
- the same module is only processed once, so it will improve build throughput (versus header file will be copied into each source file)
The C++20 standard includes a specification for modules (standard modules). Clang has a different realization of modules (Clang modules, or header modules).
Coroutine
Since C++20.
A new kind of control flow that extends the notion of a function.
A function with one of the following: co_await
, co_yield
, co_return
co_return
: "return and don’t call me again"co_yield
: "return and resume here on the next call."
A
coroutine
is simply a function that behaves differently when called multiple times: Instead of restarting from the start on each call, it will continue running after the return statement that last returned. To do this, it needs some state to store the information of where it last returned, and what the state of the local variables was at that point. In existing usage that meant that you need a program stack to store this state, but in C++ this is just stored in a struct.
Designated initializers
Since C++20.
struct Point {
double x;
double y;
double z;
};
Point point = {
.x = 3.0,
.y = 4.0,
.z = 5.0,
};
std::vector<Point> points;
points.push_back(Point{.x = 3.0, .y = 3.0});
Concept
Since C++20.
Facilities primarily intended to specify requirements ("constraints") on templates, often leading to clearer code and more straightforward error messages.
Simpler Comparisons
Defining equality (via operator==
) and ordering (via the new “spaceship” operator, operator<=>
) for user-defined types becomes much simpler. The compiler will also rewrite a != b
into !(a == b)
as needed, so there is no need to define operator!=
for your types anymore.
Ranges
Since C++20.
Similar to begin()
/end()
iterator pairs; can directly sort(data);
More Library Changes in C++20
Compile-time Evaluation
The standard library has got more constexpr
: vector, string, pair, tuple, algorithms and many other bits can now be evaluated at compile time.
String Utilities
std::string
and std::string_view
now have handy starts_with
and ends_with
members (but not yet contains
!).
String Formatting
std::format
provides a modern replacement for printf
.
Deprecation
Some C++17 features have been deprecated to allow for future changes: for example, the this pointer should no longer be captured by a lambda with [=]
, and use of the comma operator in subscripts, e.g. a[x, y, z]
, is deprecated (to allow extensions to subscript operators in future versions of C++).
Some minor C++17 features have been removed in C++20, either because they were not sufficiently useful in practice, or because better replacements have been available for some time. For example, a number of useless header files such as <cstdbool>
are no longer available, and std::uncaught_exception
is superseded by std::uncaught_exceptions
.