This gives an error:
template <class T, T A, T B>
requires A > B // <-- error
class X{};
error: parentheses are required around this expression in a requires clause
requires A < B ~~^~~ ( )
Almost all operators give this error (requires A > B
, requires A == B
, requires A & B
, requires !A
)
However &&
and ||
seem to work:
template <class T, T A, T B>
requires A && B // <-- ok
class X{};
Testes with gcc trunk and clang trunk (on May 2020) on godbolt. Both compilers give the same results.
Yes, &&
and ||
are treated special here because constraints are aware of conjunctions and disjunctions.
§ 13.5.1 Constraints [temp.constr.constr]
A constraint is a sequence of logical operations and operands that specifies requirements on template arguments. The operands of a logical operation are constraints. There are three different kinds of constraints:
(1.1) — conjunctions (13.5.1.1),
(1.2) — disjunctions (13.5.1.1), and
(1.3) — atomic constraints (13.5.1.2).
They need to be in order to define a partial ordering by constraints.
13.5.4 Partial ordering by constraints [temp.constr.order]
[Note: [...] This partial ordering is used to determine
- (2.1) the best viable candidate of non-template functions (12.4.3),
- (2.2) the address of a non-template function (12.5),
- (2.3) the matching of template template arguments (13.4.3),
- (2.4) the partial ordering of class template specializations (13.7.5.2), and
- (2.5) the partial ordering of function templates (13.7.6.2).
— end note]
Which makes this code work:
template <class It>
concept bidirectional_iterator = requires /*...*/;
template <class It>
concept randomaccess_iterator = bidirectional_iterator<It> && requires /*...*/;
template <bidirectional_iterator It>
void sort(It begin, It end); // #1
template <randomaccess_iterator It>
void sort(It begin, It end); // #2
std::list<int> l{};
sort(l.begin(), l.end()); // #A -> calls #1
std::vector<int> v{};
sort(v.begin(), v.end()); // #B -> calls #2
But for call #B
even if both sort
s are viable as both constraints (randomaccess_iterator
and bidirectional_iterator
are satisfied) the sort #2
(the one with the randomaccess_iterator
) is correctly chosen because it is more constrained than sort #1
(the one with bidirectional_iterator
) because randomaccess_iterator
subsumes bidirectional_iterator
:
See How is the best constrained function template selected with concepts?