Many of these descriptions and examples come from various resources (see Acknowledgements section), summarized in my own words.
C++20 includes the following new language features:
C++20 includes the following new library features:
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.
// `T` is not limited by any constraints.
template <typename T>
concept AlwaysSatisfied = true;
// Limit `T` to integrals.
template <typename T>
concept Integral = std::is_integral_v<T>;
// Limit `T` to both the `Integral` constraint and signedness.
template <typename T>
concept SignedIntegral = Integral<T> && std::is_signed_v<T>;
// Limit `T` to both the `Integral` constraint and the negation of the `SignedIntegral` constraint.
template <typename T>
concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;
There are a variety of syntactic forms for enforcing concepts:
// Forms for function parameters:
// `T` is a constrained type template parameter.
template <MyConcept T>
void f(T v);
// `T` is a constrained type template parameter.
template <typename T>
requires MyConcept<T>
void f(T v);
// `T` is a constrained type template parameter.
template <typename T>
void f(T v) requires MyConcept<T>;
// `v` is a constrained deduced parameter.
void f(MyConcept auto v);
// `v` is a constrained non-type template parameter.
template <MyConcept auto v>
void g();
// Forms for auto-deduced variables:
// `foo` is a constrained auto-deduced value.
MyConcept auto foo = ...;
// Forms for lambdas:
// `T` is a constrained type template parameter.
auto f = []<MyConcept T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []<typename T> requires MyConcept<T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []<typename T> (T v) requires MyConcept<T> {
// ...
};
// `v` is a constrained deduced parameter.
auto f = [](MyConcept auto v) {
// ...
};
// `v` is a constrained non-type template parameter.
auto g = []<MyConcept auto v> () {
// ...
};
The requires
keyword is used either to start a requires clause or a requires expression:
template <typename T>
requires MyConcept<T> // `requires` clause.
void f(T);
template <typename T>
concept Callable = requires (T f) { f(); }; // `requires` expression.
template <typename T>
requires requires (T x) { x + x; } // `requires` clause and expression on same line.
T add(T a, T b) {
return a + b;
}
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.
template <typename T>
concept Callable = requires (T f) { f(); };
- Type requirements - denoted by the
typename
keyword followed by a type name, asserts that the given type name is valid.
struct Foo {
int foo;
};
struct Bar {
using value = int;
value data;
};
struct Baz {
using value = int;
value data;
};
// Using SFINAE, enable if `T` is a `Baz`.
template <typename T, typename = std::enable_if_t<std::is_same_v<T, Baz>>>
struct S {};
template <typename T>
using Ref = T&;
template <typename T>
concept C = requires {
// Requirements on type `T`:
typename T::value; // A) has an inner member named `value`
typename S<T>; // B) must have a valid class template specialization for `S`
typename Ref<T>; // C) must be a valid alias template substitution
};
template <C T>
void g(T a);
g(Foo{}); // ERROR: Fails requirement A.
g(Bar{}); // ERROR: Fails requirement B.
g(Baz{}); // PASS.
- Compound requirements - an expression in braces followed by a trailing return type or type constraint.
template <typename T>
concept C = requires(T x) {
{*x} -> typename T::inner; // the type of the expression `*x` is convertible to `T::inner`
{x + 1} -> std::Same<int>; // the expression `x + 1` satisfies `std::Same<decltype((x + 1))>`
{x * 1} -> T; // the type of the expression `x * 1` is convertible to `T`
};
- Nested requirements - denoted by the
requires
keyword, specify additional constraints (such as those on local parameter arguments).
template <typename T>
concept C = requires(T x) {
requires std::Same<sizeof(x), size_t>;
};
See also: concepts library.
Concepts are also provided by the standard library for building more complicated concepts. Some of these include:
Core language concepts:
Same
- specifies two types are the same.DerivedFrom
- specifies that a type is derived from another type.ConvertibleTo
- specifies that a type is implicitly convertible to another type.Common
- specifies that two types share a common type.Integral
- specifies that a type is an integral type.DefaultConstructible
- specifies that an object of a type can be default-constructed.
Comparison concepts:
Boolean
- specifies that a type can be used in Boolean contexts.EqualityComparable
- 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
andEqualityComparable
.
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.
- 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...
Anthony Calandra
See: https://github.com/AnthonyCalandra/modern-cpp-features/graphs/contributors
MIT