C++ Logo

std-discussion

Advanced search

Re: std::apply and adl get

From: Jordi Vilar <development_at_[hidden]>
Date: Wed, 29 Sep 2021 13:50:58 +0200
Missatge de Peter Sommerlad (C++) via Std-Discussion
<std-discussion_at_[hidden]> del dia dt., 28 de set. 2021 a les
21:21:
>
> As the author of the initial spec for apply (after its implementation
> was available as an example in the standard, I think that when apply was
> specified, structured bindings weren't invented yet, and nobody thought
> about applying apply to something that is not a tuple. I would say that
> structured bindings were the reason to expand the possibilities to
> non-tuples and allowing therefore the customization.
>
> So the first question, what is your use case? and why? Is it just for
> symmetry? Or is there something useful.

I understand that structured binding is newer and intended for a more
general case, but also std::apply is usable not just by std::tuple but
also for std:array and std::pair, and technically, only requires the
same tuple_size, tuple_element and get as structured binding does to
work for any other type. I also understand that std::apply will never
work for aggregates, as this requires compiler magic.

But there are cases in which you could take advantage of being able to
deconstruct a type (other than std::tuple, std::pair and std::array,
not plain aggregates) with std::apply. Basically, the same reasons for
enabling a type customize the way structured binding deconstructs it.
But there is a case where structured binding doesn't work (yet, I
expect to see it working in a near future) that is for a variadic
case. Currently, deconstructing in a variadic case is only available
with std::apply.

In my case I have std::array-like types (some of them derive directly
from std::array, others not), and it would be very useful to apply
some algorithms with std::apply. And provided that those types already
can be decomposed with structured binding, it makes no sense to create
a custom apply function that is identical to the standard one just
changing `std::get` by `get`.

Missatge de Giuseppe D'Angelo via Std-Discussion
<std-discussion_at_[hidden]> del dia dc., 29 de set. 2021 a les
11:23:
>
> This is in general an unsolved problem -- there isn't a way to
> programmatically destructure a type. This affects std::apply, but also
> other tuple-related functionality (make_from_tuple, tuple's assignment
> operator, etc.).
>
> Note that simply making those `get` calls perform ADL won't help in
> every case where you can apply structured bindings. It won't work for
> arrays, it won't work for non-destructurable classes (where you
> destructure the accessible data members), and it also won't work for
> destructurable classes that chose a member get() vs. a non-member one.

Well, deconstruction is a strong word. In general, what is expected
from structured binding customization is a view of the pieces that
define the state of the object, not exactly the pieces that make the
object itself, as in the customization you map your class internals to
the elements to bind, maybe you decide to hide some details or
transform others in some way. This customization is great and is
already available for the structured binding. In general, I'm not
interested in this path, only in the option to enable a type to be
deconstructable.

But while structured binding allows 'deconstruction' in a
syntactically clear and simple way, its simple and efficient approach
makes hard to combine with other parts of the language. For instance,
if you want to sum all the members:

/// with structured binding
auto& [x, y, z, w] = vector;
auto sum = x + y + z + w;

/// with std::apply
auto sum = std::apply([](const auto&... xyzw) { return (xyzw + ...); }, vector);

std::apply allows handling variadics, folding, and anything you can
have in a lambda.
structured binding not even allows you to capture the names in
lambdas, nor properly decide which member is bound by value or by
reference or ignore any. Even `auto&[...]=...` wont compile if any
element is a reference, you are forced then to use `auto&&[...] =
...`.

So structured binding is a great tool for simple deconstruction, with
built-in support in the compiler for aggregates, but it is NOT
suitable for generic types (at least, right now). In the other hand,
std::apply is a more powerful tool, but now artificially limited to
tuples, pairs and arrays. Maybe if someday structured binding allows
for variadics and allows lambdas to capture names without requiring
them to be actual variables, std::apply will lose some of its
advantages over structured binding. But right now, I would find
std::apply more versatile and powerful if the restriction to use
`std::get` instead `get` by ADL was removed.

Jordi

Received on 2021-09-29 06:51:15