2

The following code is an usage example of the requires-clause:

#include <type_traits>

template <typename T>
  requires std::is_integral_v<T>
void take_integral(T value);

It accepts an expression that evalutes to a bool value (std::is_integral_v<T> in this case), and works as expected. However, when such expression is negated with the ! operator, it results in a compilation error:

#include <type_traits>

template <typename T>
  requires !std::is_integral_v<T>
void take_integral(T value);

Diagnostic from GCC:

<source>:4:12: error: expression must be enclosed in parentheses
    4 |   requires !std::is_integral_v<T>
      |            ^~~~~~~~~~~~~~~~~~~~~~
      |            (                     )
Compiler returned: 1

Diagnostic from Clang:

<source>:4:12: error: parentheses are required around this expression in a requires clause
  requires !std::is_integral_v<T>
           ^~~~~~~~~~~~~~~~~~~~~~
           (                     )
1 error generated.
Compiler returned: 1

Why are parentheses needed here?

Mário Feroldi
  • 3,463
  • 2
  • 24
  • 49
  • 1
    See also [Why do we require requires requires?](https://stackoverflow.com/q/54200988/2069064) – Barry Mar 29 '20 at 13:45

1 Answers1

5

Parentheses are required because they avoid language parsing ambiguities.

Not every expression is allowed inside a requires-clause. In fact, the standard gives an example of how a parsing ambiguity would arise if all expressions were allowed:

[temp.pre]/9

[...] The expression in a requires-clause uses a restricted grammar to avoid ambiguities. Parentheses can be used to specify arbitrary expressions in a requires-clause. [ Example:

template<int N> requires N == sizeof new unsigned short
int f();            // error: parentheses required around == expression

— end example ]

In the above example given by the standard, a compiler couldn't possibly know whether the sizeof part should be parsed as a sizeof of the expression new unsigned short or new unsigned short int. Putting parentheses around it, like requires (N == sizeof new unsigned short), solves the problem.

Mário Feroldi
  • 3,463
  • 2
  • 24
  • 49
  • 1
    "parentheses around expressions [...] changes the way constraints are evaluated." No, it doesn't... outside of the obvious cases where parentheses make it valid to begin with or you're using parentheses to affect operator precedence. – Barry Mar 29 '20 at 13:45
  • 1
    https://godbolt.org/z/Xai_8K is a short demonstration that proves that `(A || B)` "does disjunction". – Casey Mar 29 '20 at 21:15
  • 1
    It absolutely [does disjunction](http://eel.is/c++draft/temp.constr#normal-1.1), [demo](https://godbolt.org/z/t6DFej). – Barry Mar 29 '20 at 21:15
  • 1
    @Barry I'd call that more a demonstration that `(A || B)` participates in subsumption than performs disjunction, but it certainly does indicate that `(A || B)` isn't an atomic constraint expression. – Casey Mar 29 '20 at 21:18
  • @Casey I thought that's the "doesn't do disjunction" that he was going for. – Barry Mar 29 '20 at 21:18
  • [No, they're really the same.](https://godbolt.org/z/RdcNuE). – Barry Mar 29 '20 at 21:56
  • I've removed the incorrect parts of the answer. Thanks for the heads-up. – Mário Feroldi Mar 31 '20 at 02:53