2026-03-14
I got annoyed and began reading through the cpp features github page. It's not bad per se. Here's a dump:
I'm not mentioning stuff that got deprecated later on, or stuff that's trivial.
See move semantics
See std::forward
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.
See the note on memory model
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.2Used for inferring the type of an expression not known apriori.
If marked on virtual functions, they cannot be overridden. If marked on classes/structs, they cannot be inherited from.
Allows you to choose the overload based on the type of
*this. Supports const, lvalues, and rvalues (probably
others as well).
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.
0b1100 are valid binary numbers.
While auto deduces the type, this form preserves the
reference and cv-qualifier status.
This is done over a parameter pack. For unary operators it is
(e ++ ...)
(++ ... e)Similarly for binary operators it is
(cout << ... << e).
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{
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);
}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 typeVariants can hold multiple types. Optional is like option monad. They have specialized operators and functions.
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.
Provides a unified method of interacting with the underlying host OS filesystem.
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.
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 }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]] 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.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 = ...;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]] are compiler
hints that may optimize a particular branch.
char8_tStandard type for a character. Similarly, char16_t and
char32_t exist for larger characters.
Allows one to format a string the same way sprintf and
asprintf do. Format specifiers are also used.
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> headerThis header provides convenience functions like
popcount. We also have bit_cast for safe
reinterpret of one type to another.
These are used as s.starts_with and
s.ends_with.
std::midpoint provides a safe way to find the mid point
of two integers without overflow.
Partial function application :) Binds the first n arguments.