Cool C++ features
C++ 11
I'm not mentioning stuff that got deprecated later on, or stuff that's trivial.
std::move/Move semantics/Rvalue reference
See move semantics
std::forward
See std::forward
Type traits/Static Asserts
Type traits are a compile time checking/querying of template types. This is a library feature. Static asserts are a language feature allowing you to do compile time checks of things that are compile time evaluatable.
Memory model
See Memory model
Variadic templates
Allow for templates with a variable number of template arguments. ...
creates a parameter
pack or expands it. Can be useful for iterating over template arguments:
template <typename First, typename... Args> 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
decltype
Used for inferring the type of an expression not known apriori.
Final
If marked on virtual functions, they cannot be overridden. If marked on classes/structs, they cannot be inherited from.
Reference Qualified members
Allows you to choose the overload based on the type of *this
. Supports const, lvalues, and
rvalues (probably others as well).
noexcept
This is used to annotate functions that don't throw an exception. If an exception is thrown in
their call stack, std::terminate
is called instead.
C++ 14
Binary literals
0b1100
are valid binary numbers.
decltype(auto)
While auto
deduces the type, this form preserves the reference and cv-qualifier status.
C++ 17
Folding expressions
This is done over a parameter pack. For unary operators it is (e ++ ...)
or
(++ ... e)
Similarly for binary operators it is (cout << ... << e)
.
Structured bindings
Pattern matching basically. Use as follows:
int a[2] = {1, 2}; // also works for std::array, std::tuple, std::initializer_list, std::pair auto [x, y] = a; // size must match auto &[xr, yr] = a; // by reference
initialization in branches
{ std::lock_guard<std::mutex> lk(mx); if (v.empty()) v.push_back(val); } // vs. if (std::lock_guard<std::mutex> lk(mx); v.empty()) { v.push_back(val); }
Class Template Argument Deduction
This helps the compiler in deducing the template argument types using the arguments given to the constructor of the template class.
template <typename T> struct container { container(T t) {} template <typename Iter> container(Iter beg, Iter end); }; // deduction guide template <typename Iter> container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>; container a{ 7 }; // OK: deduces container<int> std::vector<double> v{ 1.0, 2.0, 3.0 }; auto b = container{ v.begin(), v.end() }; // OK: deduces container<double> container c{ 5, 6 }; // ERROR: std::iterator_traits<int>::value_type is not a type
TODO Variant and optional
Variants can hold multiple types. Optional is like option monad. They have specialized operators and functions.
String view
Kind of like &str
in Rust, provides a non-owning reference to the string, for modification to
be done on it. Most useful for trimming.
std::filesystem
Provides a unified method of interacting with the underlying host OS filesystem.
std::byte
This is supposed to be the standard way of representing a byte. This only has overloads for bitwise operations, and is therefore more in line with how a byte should behave. Can be typecasted to one of the arithmetic types.
RB-Tree splicing
This allows you to cheaply remove nodes from one map and insert it to another. The method
name is set::extract
and it takes an iterator or a key as an argument. This also returns the
object node so it can outlive the container. Also allows one to cheaply remove the node,
change its key, and reinsert it.
std::map<int, string> 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" } }
For merging one set into another, set::merge
is also provided. It works like this.
std::set<int> src {1, 3, 5}; std::set<int> dst {2, 4, 5}; dst.merge(src); // src == { 5 } // duplicates are preserved // dst == { 1, 2, 3, 4, 5 }
Fold operations: reduce and transformreduce
These allow you to operate over containers the way functional programming does. This is done
in parallel so operators need to be commutative and associative. Else just use std::accumulate
.
const std::array<int, 3> a{ 1, 2, 3 }; std::reduce(std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6 std::transform_reduce(std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten);
nodiscard and maybeunused
[[nodiscard]]
is used to imply that the return value must not be ignored. It can also be applied on types.[[maybe_unused]]
means that the parameter maybe unused so there should not be a warning for that.
C++ 20
Concepts
This is like trait bounds in Rust and typeclasses in haskell, as it encapsulates the behaviour and requirements that template arguments should possess.
// Forms for function parameters: // `T` is a constrained type template parameter. template <my_concept T> void f(T v); // `T` is a constrained type template parameter. template <typename T> requires my_concept<T> void f(T v); // `T` is a constrained type template parameter. template <typename T> void f(T v) requires my_concept<T>; // `v` is a constrained deduced parameter. void f(my_concept auto v); // `v` is a constrained non-type template parameter. template <my_concept auto v> void g(); // Forms for auto-deduced variables: // `foo` is a constrained auto-deduced value. my_concept auto foo = ...;
Three way comparison
We have a three way comparison operator that returns a type of ordering that is used to decide all of other relational operators at once.
The three orderings are:
std::strong_ordering
: This differentiates between objects being identical and equal.std::weak_ordering
: The regular one.std::partial_ordering
: This allows for objects to not be orderable as well.
struct foo { int a; bool b; char c; // Compare `a` first, then `b`, then `c` ... auto operator<=>(const foo&) const = default; }; foo f1{0, false, 'a'}, f2{0, true, 'b'}; f1 < f2; // == true f1 == f2; // == false f1 >= f2; // == false
likely and unlikely attributes
[[likely]]
and [[unlikely]]
are compiler hints that may optimize a particular branch.
char8t
Standard type for a character. Similarly, char16_t
and char32_t
exist for larger characters.
std::format
Allows one to format a string the same way sprintf
and asprintf
do. Format specifiers are also
used.
std::span
A non-owning view of a container. This can be dynamically sized or statically sized. Static ones
benefit from bounds-checking. Use as std::span<T, size>
or std::span<T>
.
<bit> header
This header provides convenience functions like popcount
. We also have bit_cast
for safe
reinterpret of one type to another.
prefix and suffix checks on strings
These are used as s.starts_with
and s.ends_with
.
std::midpoint
std::midpoint
provides a safe way to find the mid point of two integers without overflow.
bind_front
Partial function application :) Binds the first \(n\) arguments.