Namespaces
Variants
Views
Actions

Requires expression (since C++20)

From cppreference.com
< cpp‎ | language
Revision as of 00:09, 18 August 2023 by Cesar (Talk | contribs)

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

Yields a prvalue expression of type bool that describes the constraints.

Contents

Syntax

requires { requirement-seq }
requires ( parameter-list (optional) ) { requirement-seq }
parameter-list - a comma-separated list of parameters like in a function declaration, except that default arguments are not allowed and it cannot end with an ellipsis (other than one signifying a pack expansion). These parameters have no storage, linkage or lifetime, and are only used to assist in specifying requirements. These parameters are in scope until the closing } of the requirement-seq.
requirement-seq - sequence of requirements, each requirement is one of the following:
  • simple requirement
  • type requirements
  • compound requirements
  • nested requirements

Explanation

Requirements may refer to the template parameters that are in scope, to the local parameters introduced in the parameter-list, and to any other declarations that are visible from the enclosing context.

The substitution of template arguments into a requires-expression used in a declaration of a templated entity may result in the formation of invalid types or expressions in its requirements, or the violation of semantic constraints of those requirements. In such cases, the requires-expression evaluates to false and does not cause the program to be ill-formed. The substitution and semantic constraint checking proceeds in lexical order and stops when a condition that determines the result of the requires-expression is encountered. If substitution (if any) and semantic constraint checking succeed, the requires-expression evaluates to true.

If a substitution failure would occur in a requires-expression for every possible template argument, the program is ill-formed, no diagnostic required:

template<class T>
concept C = requires
{
    new int[-(int)sizeof(T)]; // invalid for every T: ill-formed, no diagnostic required
};

If a requires-expression contains invalid types or expressions in its requirements, and it does not appear within the declaration of a templated entity, then the program is ill-formed.

Simple requirements

A simple requirement is an arbitrary expression statement that does not start with the keyword requires. It asserts that the expression is valid. The expression is an unevaluated operand; only language correctness is checked.

template<typename T>
concept Addable = requires (T a, T b)
{
    a + b; // "the expression a+b is a valid expression that will compile"
};
 
template<class T, class U = T>
concept Swappable = requires(T&& t, U&& u)
{
    swap(std::forward<T>(t), std::forward<U>(u));
    swap(std::forward<U>(u), std::forward<T>(t));
};

A requirement that starts with the keyword requires is always interpreted as a nested requirement. Thus a simple requirement cannot start with an unparenthesized requires-expression.

Type requirements

A type requirement is the keyword typename followed by a type name, optionally qualified. The requirement is that the named type is valid: this can be used to verify that a certain named nested type exists, or that a class template specialization names a type, or that an alias template specialization names a type. A type requirement naming a class template specialization does not require the type to be complete.

template<typename T>
using Ref = T&;
 
template<typename T>
concept C = requires
{
    typename T::inner; // required nested member name
    typename S<T>;     // required class template specialization
    typename Ref<T>;   // required alias template substitution
};
 
template<class T, class U>
using CommonType = std::common_type_t<T, U>;
 
template<class T, class U>
concept Common = requires (T&& t, U&& u)
{
    typename CommonType<T, U>; // CommonType<T, U> is valid and names a type
    { CommonType<T, U>{std::forward<T>(t)} }; 
    { CommonType<T, U>{std::forward<U>(u)} }; 
};

Compound requirements

A compound requirement has the form

{ expression } noexcept(optional) return-type-requirement (optional) ;
return-type-requirement - -> type-constraint

and asserts properties of the named expression. Substitution and semantic constraint checking proceeds in the following order:

1) Template arguments (if any) are substituted into expression;
2) If noexcept is used, expression must not be potentially throwing;
3) If return-type-requirement is present, then:
a) Template arguments are substituted into the return-type-requirement;
b) decltype((expression)) must satisfy the constraint imposed by the type-constraint. Otherwise, the enclosing requires-expression is false.
template<typename T>
concept C2 = requires(T x)
{
    // the expression *x must be valid
    // AND the type T::inner must be valid
    // AND the result of *x must be convertible to T::inner
    {*x} -> std::convertible_to<typename T::inner>;
 
    // the expression x + 1 must be valid
    // AND std::same_as<decltype((x + 1)), int> must be satisfied
    // i.e., (x + 1) must be a prvalue of type int
    {x + 1} -> std::same_as<int>;
 
    // the expression x * 1 must be valid
    // AND its result must be convertible to T
    {x * 1} -> std::convertible_to<T>;
};

Nested requirements

A nested requirement has the form

requires constraint-expression ;

It can be used to specify additional constraints in terms of local parameters. The constraint-expression must be satisfied by the substituted template arguments, if any. Substitution of template arguments into a nested requirement causes substitution into the constraint-expression only to the extent needed to determine whether the constraint-expression is satisfied.

template<class T>
concept Semiregular = DefaultConstructible<T> &&
    CopyConstructible<T> && CopyAssignable<T> && Destructible<T> &&
requires(T a, std::size_t n)
{  
    requires Same<T*, decltype(&a)>; // nested: "Same<...> evaluates to true"
    { a.~T() } noexcept; // compound: "a.~T()" is a valid expression that doesn't throw
    requires Same<T*, decltype(new T)>; // nested: "Same<...> evaluates to true"
    requires Same<T*, decltype(new T[n])>; // nested
    { delete new T }; // compound
    { delete new T[n] }; // compound
};

Note

The keyword requires is also used to introduce requires clauses.

template<typename T>
concept Addable = requires (T x) { x + x; }; // requires-expression
 
template<typename T> requires Addable<T> // requires-clause, not requires-expression
T add(T a, T b) { return a + b; }
 
template<typename T>
    requires requires (T x) { x + x; } // ad-hoc constraint, note keyword used twice
T add(T a, T b) { return a + b; }

Keywords

requires

See also

Constraints and concepts(C++20) specifies the requirements on template arguments[edit]