Date: Sat, 30 Nov 2024 18:26:14 -0500
On Sat, Nov 30, 2024 at 9:11 AM Desmond Gold via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> This proposal introduces a *declaration requirement* in the body of a
> 'requires' expression.
>
> motivation:
>
> + It is frustrating to not have the declaration to be SFINAE-friendly.
> Hence, you can't check whether the structured binding is valid or not. This
> resorts to having hacky workarounds such as what boost.pfr did.
>
> + We didn't have features before in SFINAE to check for valid variable
> initialization except for pr-value object construction or placement-new
> expression
>
> + It is also frustrating to use lengthy types
> to be used in type requirements.
>
> requires {
> requires ValidOne<typename foo<T>::here_with<U>::there>;
> requires ValidTwo<typename foo<T>::here_with<U>::there>;
> };
>
> proposal:
>
> + adding 'declaration requirement' as one of the requirements in body
> of a 'requires' expression.
>
> + Declaration requirement (decl-req.)
> - type alias declaration
> - simple declaration (variable and structured binding declarations)
> - no linkage, storage, or lifetime
> - 'constexpr' and 'constinit' not allowed
> - allows initializers (unevaluated)
> - it also allows constrained auto as an additional nested requirement
>
How would you resolve the ambiguity between a declaration and an expression
when used as a requirement?
The usual rule is to disambiguate in favour of a declaration. However, at
present, such constructs are always interpreted as expressions when used as
requirements (because there is no other option).
> - does not allow attributes
> - the declaration requirement checks for valid declaration and after
> that, it introduces a new name in that declaration.
> - the initialization are all unevaluated
> - this name can be used in subsequent requirements within the same
> body of 'requires' expression
>
> examples:
>
> *(assume all these requires-expressions are within the template context)*
>
> 1.) introduces type alias
>
> requires {
> // requires the type to be valid and then introduces 'There'
> using There = foo<T>::here_with<U>::there;
> requires ValidOne<There>;
> requires ValidTwo<There>;
> };
>
> 2.) variables introduced in decl-req. are not the same as local parameters
>
> requires (A a, B b) {
> do_something(a, b);
> };
>
> requires {
> // this requires 'a' and 'b' to be default initialized
> A a;
> B b;
> do_something(a, b);
> };
>
> 3.) with initializers and auto
>
> requires {
> A a {}; // ok
> A _ {}; // ok
> B _ {}; // placeholder variable permitted, ok
> { use_this(a) } -> valid_return; // ok
> { use_this(_) } -> valid_return; // error, ambiguous _
>
> // functions pointers allowed
> auto (* fp) (A, B) -> void;
>
> auto c = return_this(); // ok
> some_constraint auto d = return_this(); // ok, checks if 'd' satisfies
> 'some_constraint'
>
> A& e = std::declval<A&>(); // allows unevaluated initializers
> };
>
> 4.) structured bindings
>
> requires {
> // checks if 'T' can be decomposed into three elements
> auto [a, b, c] = std::declval<T>();
> };
>
> template <typename T>
> concept decomposable = requires
> {
> auto [...args] = std::declval<T>();
> // 'args' pack can be used in other requirements
> // this is only allowed when the initializer is templated entity
> };
>
> 5.) constexpr not allowed
>
> requires
> {
> T t; // ok, if that initialization is allowed
> const U u; ok, if that initialization is allowed
> constexpr V v = some_initializer(); // error, 'constexpr' not allowed
>
> use_nttp<N>; // ok, if 'N' is some non-type template parameter
> use_nttp<u>; // error, unevaluated variable 'u' cannot be used as nttp
>
> use_ttp<decltype(u)>; // ok
> };
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
std-proposals_at_[hidden]> wrote:
> This proposal introduces a *declaration requirement* in the body of a
> 'requires' expression.
>
> motivation:
>
> + It is frustrating to not have the declaration to be SFINAE-friendly.
> Hence, you can't check whether the structured binding is valid or not. This
> resorts to having hacky workarounds such as what boost.pfr did.
>
> + We didn't have features before in SFINAE to check for valid variable
> initialization except for pr-value object construction or placement-new
> expression
>
> + It is also frustrating to use lengthy types
> to be used in type requirements.
>
> requires {
> requires ValidOne<typename foo<T>::here_with<U>::there>;
> requires ValidTwo<typename foo<T>::here_with<U>::there>;
> };
>
> proposal:
>
> + adding 'declaration requirement' as one of the requirements in body
> of a 'requires' expression.
>
> + Declaration requirement (decl-req.)
> - type alias declaration
> - simple declaration (variable and structured binding declarations)
> - no linkage, storage, or lifetime
> - 'constexpr' and 'constinit' not allowed
> - allows initializers (unevaluated)
> - it also allows constrained auto as an additional nested requirement
>
How would you resolve the ambiguity between a declaration and an expression
when used as a requirement?
The usual rule is to disambiguate in favour of a declaration. However, at
present, such constructs are always interpreted as expressions when used as
requirements (because there is no other option).
> - does not allow attributes
> - the declaration requirement checks for valid declaration and after
> that, it introduces a new name in that declaration.
> - the initialization are all unevaluated
> - this name can be used in subsequent requirements within the same
> body of 'requires' expression
>
> examples:
>
> *(assume all these requires-expressions are within the template context)*
>
> 1.) introduces type alias
>
> requires {
> // requires the type to be valid and then introduces 'There'
> using There = foo<T>::here_with<U>::there;
> requires ValidOne<There>;
> requires ValidTwo<There>;
> };
>
> 2.) variables introduced in decl-req. are not the same as local parameters
>
> requires (A a, B b) {
> do_something(a, b);
> };
>
> requires {
> // this requires 'a' and 'b' to be default initialized
> A a;
> B b;
> do_something(a, b);
> };
>
> 3.) with initializers and auto
>
> requires {
> A a {}; // ok
> A _ {}; // ok
> B _ {}; // placeholder variable permitted, ok
> { use_this(a) } -> valid_return; // ok
> { use_this(_) } -> valid_return; // error, ambiguous _
>
> // functions pointers allowed
> auto (* fp) (A, B) -> void;
>
> auto c = return_this(); // ok
> some_constraint auto d = return_this(); // ok, checks if 'd' satisfies
> 'some_constraint'
>
> A& e = std::declval<A&>(); // allows unevaluated initializers
> };
>
> 4.) structured bindings
>
> requires {
> // checks if 'T' can be decomposed into three elements
> auto [a, b, c] = std::declval<T>();
> };
>
> template <typename T>
> concept decomposable = requires
> {
> auto [...args] = std::declval<T>();
> // 'args' pack can be used in other requirements
> // this is only allowed when the initializer is templated entity
> };
>
> 5.) constexpr not allowed
>
> requires
> {
> T t; // ok, if that initialization is allowed
> const U u; ok, if that initialization is allowed
> constexpr V v = some_initializer(); // error, 'constexpr' not allowed
>
> use_nttp<N>; // ok, if 'N' is some non-type template parameter
> use_nttp<u>; // error, unevaluated variable 'u' cannot be used as nttp
>
> use_ttp<decltype(u)>; // ok
> };
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
-- *Brian Bi*
Received on 2024-11-30 23:26:29