-3

From questions like this, from C++20 - The Complete Guide, and even from cppreference, my understanding is that the keyword requires can do one of 2 things only:

and I also think I've understand more or less what they are for, thanks to linked sources.

However, I'm puzzled by the use of requires inside a requires-expression, e.g.

template<typename T>
… requires {
  requires std::is_const_v<T>;
}

From the standard draft, I read that a requires-expression (e.g. the one introduced by the first requires in the snippet above) must have a requirement-body, which must in turn be a { requirement-seq }, i.e. something between curly braces, which is not the case of std::is_const_v<T>;, from which I deduce that requires std::is_const_v<T>; is a requires-clause, that should look like this

requires constraint-logical-or-expression

However, [expr.prim.req.nested] tells me that a nested-requirement looks like this:

requires constraint-expression;

So maybe use of requires nested in a requires-expression is not a requires-clause?

If it is, I think the difference between the two quoted grammars above should mean that nested-requirements are a subset of requires-clauses, in which case I should be able to see, following the various cross-references, that a constraint-expression is a constraint-logical-or-expression but not viceversa. Now I see that

And a constraint-expression is a logical-expression too.

But what I don't understand is where the parenthesis are gone.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • from the linked `require-expression` it looks like it just a `nested-requirement`. I don't quite get the rest of question tbh. – apple apple Mar 04 '23 at 12:21
  • `requires-expression` -> `requires requirement-parameter-list(opt) requirement-body` -> `requires requirement-body` -> `requires {requirement-seq}` -> `requires {requirement}` -> **`requires {nested-requirement}` -> `requires {requires constraint-expression;}`** – apple apple Mar 04 '23 at 12:23
  • @appleapple, not sure if it helps, but I had made a typo in the paragraph before the bullet list. Fixed now. – Enlico Mar 04 '23 at 13:32
  • To use your notation, I'm saying that, on the one hand, `requires !std::is_const_v;` is a `nested-requirement` -> `requires constraint-expression;` -> `requires logical-or-expression;`; on the other hand, `requires-clause` -> `requires constraint-logical-or-expression` -> `requires constraint-logical-and-expression` -> `requires primary-expression` -> `requires ( expression )` -> `requires ( assignment-expression )` -> `requires ( conditional-expression )` -> `requires ( logical-or-expression )`. – Enlico Mar 04 '23 at 13:39
  • So if it is true that `requires !std::is_const_v;` is a `nested-requirement`, it should have the form of `requires logical-or-expression;`, whereas if it is a `requires-clause`, it should have the form of `requires ( logtical-or-expression )`. I think it is a `requires-clause` (isn't it?) and, more precisely, a `nested-requirement`. If that's the case, I see a mismatch between it being `requires ( logical-or-expression )` and also a `requires logical-or-expression;`. – Enlico Mar 04 '23 at 13:43
  • well it doesn't have `()` shows it cannot derived from that lexical group (i.e. it's not (at least directly) a `require-clause`). just like a `[i]` can both be a beginning of lambda or subscript operator. – apple apple Mar 04 '23 at 13:49
  • @‌Enlico fwiw, as you linked, the `require-clause` (with the form `requires ( expression )`) is for `template requires (!std::is_const_v) void foo(){}` – apple apple Mar 04 '23 at 14:00
  • @appleapple, I've removed the `!` as it just adds a complication that my question is not about. – Enlico Mar 04 '23 at 14:45
  • @appleapple, I don't understand the first comment with the `()`. – Enlico Mar 04 '23 at 14:46
  • @appleapple, whether `[i]` is the beginning of a lambda or subscript-based indexing, the square brackets are there. But `requires std::is_const_v;` is valid in the snippet in my question, and it looks to me like it has not the form of a `requires-clause`, because it lacks the parenthesis. I must be wrong, and I'd like to understand how. – Enlico Mar 04 '23 at 14:53
  • yes, it's not the best example. I was trying to make the point that they're just different, you don't parse a program by see just part of it. – apple apple Mar 04 '23 at 14:57
  • Apparently (from the answer) my understanding that a _nested-requirement_ is a _requires-clause_ is wrong. – Enlico Mar 04 '23 at 15:00
  • I'm not even sure how to answer this. Of course a nested `requires ...;` is neither a *requires-clause* nor a *requires-expression*. You seem to have a misunderstand of how the grammar works. You can't just take a random definition from the middle of the grammar and match it against a random piece of code (like you do in the second part of the question). You must start with [*translation-unit*](http://eel.is/c++draft/basic.link#nt:translation-unit) (the top-level symbol of the grammar) and match it to the whole source file, gradually descending to the desired element. – HolyBlackCat Mar 04 '23 at 15:02
  • @Enlico well, it's not a `require-expression` either, and actually it can also be subpart of `require-clause` (within sub `require-expression`). – apple apple Mar 04 '23 at 15:02
  • for example `template requires requires{ requires !std::is_const_v; } void foo(){}` – apple apple Mar 04 '23 at 15:06
  • @HolyBlackCat, I'm not taking any random piece from anywhere. My question starts with _my understanding is that the keyword `requires` can do one of 2 things only: introduce a requires-clause, introduce a requires-expression_. If that's wrong in the first place (and apparently it is), is there any reason to keep reading the rest of the question? Anything can follow from wrong assumptions. So I guess I didn't need to take anything random from the middle of anywhere, no? – Enlico Mar 04 '23 at 15:11
  • What I'm saying is, you took `requires std::is_const_v;`, and tried to match it against *requires-clause*. But you can't do that. Even if you matched it successfully, it wouldn't mean anything. You need to start at the whole translation unit and match it against *translation-unit*, and then descend recursively. – HolyBlackCat Mar 04 '23 at 15:18
  • _Even if you matched it successfully, it wouldn't mean anything._ What do you mean by "matching"? Text-matching? If so, yes, ok, but the mistake is simply in the hypothesis that `requires` could introduce 1 of 2 things only. I wouldn't even have tried matching it if I knew it could introduce a 3rd thing. – Enlico Mar 04 '23 at 15:31

2 Answers2

0

A requires-clause is a grammatical construct that is only introduced by a declarator (only for templated functions), function definition, class member declarator, template header, or lambda syntax. You can see that from the grammar.

The only way to put a requires-clause inside a requires-expression is to smuggle it in via a lambda. But then, the clause applies just to that lambda.

A nested-requirement is not a requires-clause, neither grammatically nor semantically. The key difference is that a requires-clause is required to only be a series of primary expressions merged by && and ||. This is important for being able to decompose a requires-clause down into a series of atomic constraints. Nested-requirements are able to do more because a nested requirement is itself a single atomic constraint.

Put more simply, the requires-clause defines a constraint:

A constraint is a sequence of logical operations and operands that specifies requirements on template arguments.

A nested-requirement is itself an atomic constraint.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • If a _nested-requirement_ (which is introduced via `requires`) is not a _requires-clause_, then is it a _requires-expression_ (which is introduced by `requires` too)? If so, how comes it lacks the `{}` bit? If not, then should I deduce that `requires` can introduce 3 things (_requires-expression_, _requires-clause_, _nested-requirement_), and not 2 (_requires-expression_, _requires-clause_)? – Enlico Mar 04 '23 at 14:56
  • 1
    @Enlico: It is a nested-requirement. That's the name of the grammatical construct. It is neither a nested-expression nor a requires-clause. You're trying to apply formal language to answers and questions that were not themselves using formal language. – Nicol Bolas Mar 04 '23 at 15:02
  • I've understood that, and thank you for explaining. I'm just asking confirmation that this means that `requires` can be the beginning of 3 things. Or maybe there's a 4th grammatical construct starting with `requires`? – Enlico Mar 04 '23 at 15:04
0

The C++ standard never says requires can only be the beginning of requires-clause and requires-expression. It can also be the beginning of nested-requirement, which is one of the four types of requirements in the requirement body for requires-expression.

Althrough nested-requirement may looks like requires-clause in grammar, they are not the same. For example:

template <typename T>
concept CF = false;

template <typename T>
void foo()
    requires // requires clause
    requires // requires expression
{
    requires !CF<T>; // nested requirement
}
{}

template <typename T>
void bar()
    requires !CF<T> // invalid requires clause
{}

I'm not sure whether the keyword page for requires on cppreference is correct (maybe incomplete?). requires-clause and nested-requirement are totally different things. I think it's OK to say requires can be the beginning of 3 things. If you want to find all the usages for a keyword, I think the best way is to read the Index page of the C++ standard.

courage
  • 81
  • 5