template<typename T> constexpr bool get_value() { return T::value; } template<typename T> requires (sizeof(T) > 1) && (get_value<T>()) void f(T); // has associated constraint sizeof(T) > 1 ∧ get_value<T>() void f(int); f('a'); // OK: calls f(int)
template <class T> concept sad = false; template <class T> int f1(T) requires (!sad<T>); template <class T> int f1(T) requires (!sad<T>) && true; int i1 = f1(42); // ambiguous, !sad<T> atomic constraint expressions ([temp.constr.atomic]) // are not formed from the same expression template <class T> concept not_sad = !sad<T>; template <class T> int f2(T) requires not_sad<T>; template <class T> int f2(T) requires not_sad<T> && true; int i2 = f2(42); // OK, !sad<T> atomic constraint expressions both come from not_sad template <class T> int f3(T) requires (!sad<typename T::type>); int i3 = f3(42); // error: associated constraints not satisfied due to substitution failure template <class T> concept sad_nested_type = sad<typename T::type>; template <class T> int f4(T) requires (!sad_nested_type<T>); int i4 = f4(42); // OK, substitution failure contained within sad_nested_type
template <unsigned N> constexpr bool Atomic = true; template <unsigned N> concept C = Atomic<N>; template <unsigned N> concept Add1 = C<N + 1>; template <unsigned N> concept AddOne = C<N + 1>; template <unsigned M> void f() requires Add1<2 * M>; template <unsigned M> int f() requires AddOne<2 * M> && true; int x = f<0>(); // OK, the atomic constraints from concept C in both fs are Atomic<N> // with mapping similar to template <unsigned N> struct WrapN; template <unsigned N> using Add1Ty = WrapN<N + 1>; template <unsigned N> using AddOneTy = WrapN<N + 1>; template <unsigned M> void g(Add1Ty<2 * M> *); template <unsigned M> void g(AddOneTy<2 * M> *); void h() { g<0>(nullptr); // OK, there is only one g }— end example
template <unsigned N> void f2() requires Add1<2 * N>; template <unsigned N> int f2() requires Add1<N * 2> && true; void h2() { f2<0>(); // ill-formed, no diagnostic required: // requires determination of subsumption between atomic constraints that are // functionally equivalent but not equivalent }— end example
template<typename T> concept C = sizeof(T) == 4 && !true; // requires atomic constraints sizeof(T) == 4 and !true template<typename T> struct S { constexpr operator bool() const { return true; } }; template<typename T> requires (S<T>{}) void f(T); // #1 void f(int); // #2 void g() { f(0); // error: expression S<int>{} does not have type bool } // while checking satisfaction of deduced arguments of #1; // call is ill-formed even though #2 is a better match— end example
constraint-expression: logical-or-expression
template<typename T> concept C = true; template<C T> void f1(T); template<typename T> requires C<T> void f2(T); template<typename T> void f3(T) requires C<T>;
template<typename T> concept C1 = true; template<typename T> concept C2 = sizeof(T) > 0; template<C1 T> void f4(T) requires C2<T>; template<typename T> requires C1<T> && C2<T> void f5(T);
template<C1 T> requires C2<T> void f6(); template<C2 T> requires C1<T> void f7();— end example
template <class T> concept C = true; template <class T> struct A { template <class U> U f(U) requires C<typename T::type>; // #1 template <class U> U f(U) requires C<T>; // #2 }; template <> template <class U> U A<int>::f(U u) requires C<int> { return u; } // OK, specializes #2
template<typename T> concept A = T::value || true; template<typename U> concept B = A<U*>; template<typename V> concept C = B<V&>;Normalization of B's constraint-expression is valid and results in T::value (with the mapping ) ∨ true (with an empty mapping), despite the expression T::value being ill-formed for a pointer type T. Normalization of C's constraint-expression results in the program being ill-formed, because it would form the invalid type V&* in the parameter mapping. — end example
template<typename T> concept C1 = sizeof(T) == 1; template<typename T> concept C2 = C1<T> && 1 == 2; template<typename T> concept C3 = requires { typename T::type; }; template<typename T> concept C4 = requires (T x) { ++x; } template<C2 U> void f1(U); // #1 template<C3 U> void f2(U); // #2 template<C4 U> void f3(U); // #3
template<typename T> concept C1 = requires(T t) { --t; }; template<typename T> concept C2 = C1<T> && requires(T t) { *t; }; template<C1 T> void f(T); // #1 template<C2 T> void f(T); // #2 template<typename T> void g(T); // #3 template<C1 T> void g(T); // #4 f(0); // selects #1 f((int*)0); // selects #2 g(true); // selects #3 because C1<bool> is not satisfied g(0); // selects #4— end example