Why Do We Require Requires Requires

Why do we require requires requires?

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.

When is a requires clause expression required to be parenthesised? (pun by accident)

Yes, && and || are treated special here because constraints are aware of conjunctions and disjunctions.

§ 13.5.1 Constraints [temp.constr.constr]

  1. 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]


  1. [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 sorts 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?

Are concepts with only a boolean literal value ill-formed, no diagnostic required?

These are the two first examples in the "trailing requires-clause" section of the standard:

void f1(int a) requires true;               // error: non-templated function

template<typename T>
auto f2(T a) -> bool requires true; // OK

Although examples given in the standard are explicitly non-normative, these make the intent abundantly clear.

Essentially, concepts can only be applied to template functions (so your code is invalid C++), but Boolean literals (e.g., requires true) are perfectly valid.

However, templates whose requirements are never satisfied (whether by a literal false in a required clause or otherwise) seem to be ill-formed, as indicated by this example,

How to use a requires clause with lambda functor arguments?

In my humble opinion and based on Concepts TS §5.1.4/c4 Requires expressions [expr.prim.req] (Emphasis Mine):

A requires-expression shall appear only within a concept definition (7.1.7), or within the requires-clause of a template-declaration
(Clause 14) or function declaration (8.3.5).

The above quote specifically dictates the contexts where a requires clause can appear and lambdas is not one of them.

Concequently,

[](auto x) requires C1<decltype(x)> && C2<decltype(x)> {
// ...
}

Is not valid.

However, in §5.1.2 Lambda expressions [expr.prim.lambda] there's the following example:

template<typename T> concept bool C = true;
auto gl = [](C& a, C* b) { a = *b; }; // OK: denotes a generic lambda

So I guess, you could accomplish what you want in the following manner:

template <class T> concept bool C1 = true;                                        
template <class T> concept bool C2 = true;
template <class T> concept bool C3 = C1<T> && C2<T>; // Define a concept that combines
// `C1` and `C2` requirements.

int main() {
auto f = [](C3 x) { /* Do what ever */ }; // OK generic lambda that requires input
// argument satisfy `C1` and `C2`
}

Live Demo

what is the difference between required and @required in flutter. What is the difference between them and when do we need to use them?

@required is just an annotation that allows analyzers let you know that you're missing a named parameter and that's it. so you can still compile the application and possibly get an exception if this named param was not passed.

However sound null-safety was added to dart, and required is now a keyword that needs to be passed to a named parameter so that it doesn't let the compiler run if this parameter has not been passed. It makes your code more strict and safe.

If you truly think this variable can be null then you would change the type by adding a ? after it so that the required keyword is not needed, or you can add a default value to the parameter.

Concept definition requiring a constrained template member function

What you're looking for is for a way for the compiler to synthesize an archetype of Surface. That is, some private, anonymous type that minimally satisfies the Surface concept. As minimally as possible. Concepts TS doesn't currently allow for a mechanism for automatically synthesizing archetypes, so we're left with doing it manually. It's quite a complicated process, since it's very easy to come up with archetype candidates that have way more functionality that the concept specifies.

In this case, we can come up with something like:

namespace archetypes {
// don't use this in real code!
struct SurfaceModel {
// none of the special members
SurfaceModel() = delete;
SurfaceModel(SurfaceModel const& ) = delete;
SurfaceModel(SurfaceModel&& ) = delete;
~SurfaceModel() = delete;
void operator=(SurfaceModel const& ) = delete;
void operator=(SurfaceModel&& ) = delete;

// here's the actual concept
void move_to(point2f );
void line_to(point2f );
void arc(point2f, float);
// etc.
};

static_assert(Surface<SurfaceModel>());
}

And then:

template <typename T>
concept bool Drawable() {
return requires(const T& t, archetypes::SurfaceModel& surface) {
{ t.draw(surface) } -> void;
};
}

These are valid concepts, that probably work. Note that there's a lot of room for even more refinement on the SurfaceModel archetype. I have a specific function void move_to(point2f ), but the concept just requires that it's callable with an lvalue of type point2f. There's no requirement that move_to() and line_to() both take an argument of type point2f, they could both take complete different things:

struct SurfaceModel {    
// ...
struct X { X(point2f ); };
struct Y { Y(point2f ); };
void move_to(X );
void line_to(Y );
// ...
};

This kind of paranoia makes for a better archetype, and serves to illustrate how complex this problem could be.

When is 'this' required?

You need it when you have a local variable that has the exact same name as the member variable. In this case, the local variable is said to shadow the member variable. To get to the member variable in this situation, you must use this.

Some people consider it good practice to explicitly mention that the variable you are modifying is a member variable by using this all the time, but this is not always the case.

How to concisely express the C++20 concept of range containing T-typed values?

The problem here is: what is the actual constraint that you want?

Do you want to:

  • constrain on the range's value type being specifically T?
  • constrain on the range's value type being convertible to T?
  • constrain on the range's value type satisfying some other concept, C?
  • any of the above but instead about the range's reference type?

Different contexts call for different versions of this. Your range_of concept is a perfectly fine implementation of the first one of these (could use std::same_as instead of std::is_same_v but doesn't super matter). But what happens when you want something else? The specific choice of constraint is going to depend very much on what it is you want to do.

So the direct answer is: there's no range_of concept because there's really no one true answer of what that concept should do.


A different answer would be that this isn't even what would be useful. What would be useful would be a direct way to pull out the associated types of a given range to make it easy to add further constraints on them.

For instance, if we take Rust, we can define a function that takes an arbitrary iterator:

fn foo<I: Iterator>(it: I) { ... }

That's sort of akin to just void f(range auto). It's a constraint, but not a very useful one. But Iterator, in Rust, has an associated type: its Item. So if you wanted an Iterator over a specific type, that's:

fn foo<I: Iterator<Item=i32>>(it: I) { ... }

If you wanted an Iterator whose type satisfies some other constraint:

fn foo<T: Debug, I: Iterator<Item=T>>(it: I) { ... }

And it's really this ability - to pull out associated types and constrain on them directly - that we really lack.

C++ - Why is the 'template' keyword required here?

Consider:

template<typename T>
struct C
{
struct S
{
int a = 99;
};

void f(S s, int i)
{
s.a<0>(i);
}
};

template<>
struct C<long>::S
{
template<int>
void a(int)
{}
};

int main()
{
C<int>{}.f({}, 0); // #1
C<long>{}.f({}, 0); // #2
}

s.a<0>(i) is parsed as an expression containing of two comparison operations < and >, and this is fine for #1 but fails for #2.

If this is changed to s.template a<0>(i) then #2 is OK and #1 fails. Thus the template keyword is never redundant here.

MSVC is capable of interpreting the expression s.a<0>(i) both ways within the same program. But this is not correct according to the Standard; each expression should have only one parse for the compiler to deal with.



Related Topics



Leave a reply



Submit