Why do we require requires requires?
The situation is exactly analogous to noexcept(noexcept(...))
. Sure, this sounds more like a bad thing than a good thing, but let me explain. :) We'll start with what you already know:
C++11 has "noexcept
-clauses" and "noexcept
-expressions." They do different things.
A
noexcept
-clause says, "This function should be noexcept when... (some condition)." It goes on a function declaration, takes a boolean parameter, and causes a behavioral change in the declared function.A
noexcept
-expression says, "Compiler, please tell me whether (some expression) is noexcept." It is itself a boolean expression. It has no "side effects" on the behavior of the program — it's just asking the compiler for the answer to a yes/no question. "Is this expression noexcept?"
We can nest a noexcept
-expression inside a noexcept
-clause, but we typically consider it bad style to do so.
template<class T>
void incr(T t) noexcept(noexcept(++t)); // NOT SO HOT
It's considered better style to encapsulate the noexcept
-expression in a type-trait.
template<class T> inline constexpr bool is_nothrow_incrable_v =
noexcept(++std::declval<T&>()); // BETTER, PART 1
template<class T>
void incr(T t) noexcept(is_nothrow_incrable_v<T>); // BETTER, PART 2
The C++2a Working Draft has "requires
-clauses" and "requires
-expressions." They do different things.
A
requires
-clause says, "This function should participate in overload resolution when... (some condition)." It goes on a function declaration, takes a boolean parameter, and causes a behavioral change in the declared function.A
requires
-expression says, "Compiler, please tell me whether (some set of expressions) is well-formed." It is itself a boolean expression. It has no "side effects" on the behavior of the program — it's just asking the compiler for the answer to a yes/no question. "Is this expression well-formed?"
We can nest a requires
-expression inside a requires
-clause, but we typically consider it bad style to do so.
template<class T>
void incr(T t) requires (requires(T t) { ++t; }); // NOT SO HOT
It's considered better style to encapsulate the requires
-expression in a type-trait...
template<class T> inline constexpr bool is_incrable_v =
requires(T t) { ++t; }; // BETTER, PART 1
template<class T>
void incr(T t) requires is_incrable_v<T>; // BETTER, PART 2
...or in a (C++2a Working Draft) concept.
template<class T> concept Incrable =
requires(T t) { ++t; }; // BETTER, PART 1
template<class T>
void incr(T t) requires Incrable<T>; // BETTER, PART 2
I think cppreference's concepts page explains this. I can explain with "math" so to say, why this must be like this:
If you want to define a concept, you do this:
template<typename T>
concept Addable = requires (T x) { x + x; }; // requires-expression
If you want to declare a function that uses that concept, you do this:
template<typename T> requires Addable<T> // requires-clause, not requires-expression
T add(T a, T b) { return a + b; }
Now if you don't want to define the concept separately, I guess all you have to do is some substitution. Take this part requires (T x) { x + x; };
and replace the Addable<T>
part, and you'll get:
template<typename T> requires requires (T x) { x + x; }
T add(T a, T b) { return a + b; }
which explains the mechanics. The why is best illustrated with an example of the ambiguity that would result if we changed the language to accept a single requires
as a shorthand for requires requires
.
constexpr int x = 42;
template<class T>
void f(T) requires(T (x)) { (void)x; };
template<class T>
void g(T) requires requires(T (x)) { (void)x; };
int main(){
g<bool>(0);
}
View in Godbolt to see the compiler warnings, but note that Godbolt doesn't try the link step, which would fail in this case.
The only difference between f and g is the doubling up of 'requires'. Yet the semantic difference between f and g is enormous:
- g is just a function declaration, f is a full definition
- f accepts only bool, g accepts every type castable to void
- g shadows x with its own (superfluously parenthesized) x, but
- f casts the global x to the given type T
Obviously we wouldn't want the compiler to change one into the other automatically. This could have been solved by using a separate keyword for the two meanings of requires
, but when possible C++ tries to evolve without introducing too many new keywords, as that breaks old programs.
It is because the grammar requires it. It does.
A requires
constraint does not have to use a requires
expression. It can use any more-or-less arbitrary boolean constant expression. Therefore, requires (foo)
must be a legitimate requires
constraint.
A requires
expression (that thing that tests whether certain things follow certain constraints) is a distinct construct; it's just introduced by the same keyword. requires (foo f)
would be the beginning of a valid requires
expression.
What you want is that if you use requires
in a place that accepts constraints, you should be able to make a "constraint+expression" out of the requires
clause.
So here's the question: if you put requires (foo)
into a place that is appropriate for a requires constraint... how far does the parser have to go before it can realize that this is a requires constraint rather than a constraint+expression the way you want it to be?
Consider this:
void bar() requires (foo)
{
//stuff
}
If foo
is a type, then (foo)
is a parameter list of a requires expression, and everything in the {}
is not the body of the function but the body of that requires
expression. Otherwise, foo
is an expression in a requires
clause.
Well, you could say that the compiler should just figure out what foo
is first. But C++ really doesn't like it when the basic act of parsing a sequence of tokens requires that the compiler figure out what those identifiers mean before it can make sense of the tokens. Yes, C++ is context-sensitive, so this does happen. But the committee prefers to avoid it where possible.
So yes, it's grammar.