On Sat, Nov 30, 2024 at 9:11 AM Desmond Gold via Std-Proposals <std-proposals@lists.isocpp.org> 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@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals


--
Brian Bi