C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Floating an idea: Dereference operators for std::reference_wrapper

From: Jeremy Rifkin <jeremy_at_[hidden]>
Date: Wed, 25 Sep 2024 18:36:07 -0500
> It's nullable, which might have been the original motivation for
> treating it like a pointer. There's also a valid reason to have a
> checked accessor that throws, value(), and an unchecked one, with some
> alternative syntax. For reference wrapper it's not nullable and
> doesn't need to be checked, so only needs one accessor.

The original rationale for -> and * does largely revolve around the
optionality of pointers. From N1878, revision 1 for std::optional:

> The reason for this interface design is that pointers and a null
> pointer value have been used to represent absent objects from the very
> first days of C. This technique is so extensively used that the
> expressions used to access the pointed object, (*p) and p->, have an
> extraordinary power at expressing optionally: all by themselves, from syntax
> alone, they make loudly clear that the value being accessed might be absent.

Regarding value() being checked, this is a good point. Though this could
have also been done as .value_checked() or whatnot if there was
sufficient concern about pointer syntax being misleading. Revision 1
offered only ->, *, and a free function `get([const] optional<T> &)`
that didn't throw, which was removed in revision 2. A throwing observer
wasn't added until revision 3. The use of -> and * for non-pointers
appears to have been debated plenty for optional, both for the the std
proposal and the boost implementation.

The point about nullability is well-received. From my perspective,
std::optional expanded the meaning of ->, building on intuition from
pointers, and -> now seems to carry an even broader meaning of accessing
"through something else." However, I wouldn't necessarily be surprised
if I was in the minority in finding that intuitive.

> For what it's worth, we have a template that inherits from
> reference_wrapper<T> specifically for this reason. After a lot of use,
> r.get().f() is pretty verbose compared to r->f().

It's helpful to hear you're already doing something to achieve the -> syntax.

> Well that and the name reference_wrapper<T> is also very verbose so ours
> is just Ref<T>.

If I proceed a proposal in this space, maybe it would also make sense to
propose a std::reference alias for reference_wrapper. I am wary of scope
creep, though. The use of reference_wrapper does seem to have expanded
substantially from its original purpose of facilitating std::bind and
threads etc so a shorter name may be widely useful. Maybe it should even
have another means of inclusion other than <functional>, like P0472R2 is
trying to achieve with std::monostate.

On a different note, a colleague raised the point that adding an
operator-> could break some existing code in the case of concepts
checking for the existence of ->. If this would render any addition to
reference_wrapper dead on arrival, and I haven't fully investigated the
implications here, one option would be to introduce std::reference as a
new template class. This could be confusing, though, with
reference_wrapper and std::ref.

I'm also open to the idea that maybe current widespread use of
std::reference_wrapper to achieve a rebindable reference is not
idiomatic. I think it is at least clear that the ability to rebind a
reference has proven widely useful.

If reference_wrapper is to forward more operations, maybe first order
language support for rebinding references is warranted. Maybe something like
[[rebind]] ref = foo;
struct S {
    [[rebindable]] T& ref;
};
But of course this raises other considerations. I'm not sure I like
this, fwiw, just brainstorming.

Cheers,
Jeremy

On Sep 25 2024, at 11:13 am, Jonathan Wakely via Std-Proposals
<std-proposals_at_[hidden]> wrote:

>> On Wed, 25 Sept 2024, 16:48 Barry Revzin, <barry.revzin_at_[hidden]> wrote:
>>
>>>
>>>
>>>
>>>> On Wed, Sep 25, 2024, 7:30 AM Jonathan Wakely via Std-Proposals
>>>> <std-proposals_at_[hidden]> wrote:
>>>>
>>>>>
>>>>>
>>>>>
>>>>>> On Wed, 25 Sept 2024 at 05:53, Jeremy Rifkin via Std-Proposals
>>>>>> <std-proposals_at_[hidden]> wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>> It'd be useful for me to have
>>>>>>> `std::reference_wrapper::operator->` and
>>>>>>> `std::reference_wrapper::operator*`.
>>>>>>
>>>>>> Why? You didn't provide any rationale.
>>>>>>
>>>>>> Isn't get() already sufficient for everything that operator*
>>>>>> could do?
>>>>>> Do you really need operator->() when refwrap->foo() could be
>>>>>> written as refwrap.get().foo() instead?
>>>
>>> For what it's worth, we have a template that inherits from
>>> reference_wrapper<T> specifically for this reason. After a lot of
>>> use, r.get().f() is pretty verbose compared to r->f().
>>>
>>> Well that and the name reference_wrapper<T> is also very verbose so
>>> ours is just Ref<T>.
>>>
>>>
>>>
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>> As far as I can tell this has not
>>>>>>> been proposed before. It would follow the precedent of
>>>>>>> std::optional as
>>>>>>> well as the forwarding comparison operators added in P2944. Has this
>>>>>>> been considered before or are there any glaring issues with such
>>>>>>> a functionality?
>>>>>>>
>>>>>>
>>>>>> Apart from get() already existing, the main one is that it's a
>>>>>> **reference** wrapper, and you're proposing pointer-like semantics.
>>>
>>> I guess you could make the same argument about optional, which is
>>> significantly less pointer-like than reference_wrapper.
>
>
> It's nullable, which might have been the original motivation for
> treating it like a pointer. There's also a valid reason to have a
> checked accessor that throws, value(), and an unchecked one, with some
> alternative syntax. For reference wrapper it's not nullable and
> doesn't need to be checked, so only needs one accessor.
>
>
>
>
>>
>>>
>>>>
>>>>>
> --
>
> Std-Proposals mailing list
>
> Std-Proposals_at_[hidden]
>
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2024-09-25 23:36:15