C++ Logo

std-proposals

Advanced search

Re: [std-proposals] requires(auto [a, b] = /* expr */){...}

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Wed, 8 Jan 2025 15:36:29 -0500
On Wed, Jan 8, 2025 at 2:53 PM 李 秋逸 via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Hello everyone. Just as the title, there is no way to test whether an
> experession can be ructured bound. The title not looks like a good way and
> maybe the best idea will come up while discussion.
>

At first glance this looked like P0931 "Structured bindings with
polymorphic lambdas"
<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0931r0.pdf>
(Aaryaman
Sagar, 2017).
(If I were titling that paper, I would have called "Structured bindings in
parameter declarations.")
    void get_second(*auto [k,v]*) { return v; } // C++20 abbreviated
template syntax
    assert(get_second(std::make_pair(1,2)) == 2);
This would also naturally apply everywhere else in the grammar that looks
like a parameter list... except that it would apply only if the thing in
the parameter list could take `auto`. So it *wouldn't* apply to `catch`
parameters, and it *wouldn't* apply to `requires`-expression parameters,
either. Which is the case you're trying to make work. So I think P0931 ends
up being a red herring. (I'd still like to know what ever happened to it!)

You're aiming for something like this:
    template<class T> concept Pairish =
      requires (auto [k,v] = std::declval<T>()) { };
But requires-expression parameters aren't allowed to have default
arguments. (GCC buggily accepts anyway: #99511
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99511>.)
I think it's reasonable that requires-expression parameters aren't allowed
to have default arguments. We should be looking for a syntax that is more
general, like, "This *statement* is well-formed." So:
    template<class T> concept Pairish =
      requires (T t) {
        { auto [k,v] = t; }
      };
I can't think of any *statement* whose well-formedness it makes sense to
test, except for a *declaration*. For example, it is silly to write
    template<class T> concept Returnable =
      requires (T t) {
        { return t; }
      };
because we can't know if `return t` is well-formed without knowing a lot
about the context in which that statement appears (like, what's the return
type of the function it's inside).
And constructions like `throw t` and `co_yield t` are already expressions.

There is room in the grammar here:
    template<class T> concept Pairish =
      requires (T t) {
        {{ auto [k,v] = t; }} // notice the double braces
      };
But that's not very self-explanatory nor aesthetically appealing.

The narrowest possible feature here would be a magic compiler type-trait
that tells you whether `T` can be structured-bound into n parts. (And in
C++26, "into *at least* n parts," because there might be a trailing
ellipsis.)
This has come up on StackOverflow:
https://stackoverflow.com/questions/70166429/detect-if-class-has-method-that-is-suitable-for-structured-binding
I'm convinced I recall talking about such a type-trait with Michael Park,
years ago, and we designed its interface and everything... but if we did,
that iteration left no trail on the Internet — no paper, no blog post, no
nothing.
These days, it looks like the state of the art is Will Wray's request to
compiler vendors, none of whom have acted on it yet AFAICT:
- Clang: Enhancement: Please add a builtin to count bindings in
[dcl.struct.bind] <https://github.com/llvm/llvm-project/issues/46049>
- GCC: Enhancement: Please add a builtin to count bindings in
[dcl.struct.bind] <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96185>
- MSVC: Enhancement: Please add a builtin to count bindings in
[dcl.struct.bind]
<https://developercommunity.visualstudio.com/t/enhancement-please-add-a-builtin-to-count-bindings/1111203>
Richard Smith suggests that you can kinda-sorta get this information in
C++26 via:

auto num_bindings_impl(auto v) {
  auto [...xs] = v;
  return std::integral_constant<std::size_t, sizeof...(xs)>();
}template<typename T> constexpr std::size_t num_bindings =
  decltype(num_bindings_impl(std::declval<T>()))::value;

But that still requires that you know the thing is decomposable into *some*
number of parts; and it doesn't leave you in a position where you can tell
what their types are.

–Arthur

Received on 2025-01-08 20:36:45