C++ Logo

std-proposals

Advanced search

Re: Yet another member function for std::map

From: Barry Revzin <barry.revzin_at_[hidden]>
Date: Sun, 1 Aug 2021 14:52:43 -0500
On Sun, Aug 1, 2021 at 2:41 AM Fabio Alemagna <falemagn_at_[hidden]> wrote:

>
>
> Il giorno gio 29 lug 2021 alle ore 02:30 Barry Revzin via Std-Proposals <
> std-proposals_at_[hidden]> ha scritto:
>
>>
>> What would you do if value_at returned a T*? You can't do value_or() on a
>> T*, because pointers have very few operations you can do on them (and
>> nearly all of those would be straight up invalid in this use-case - which
>> does not make for a great API!) you'd either push for some language feature
>> that does that or you'd write some non-member function that handles this
>> case. Which would be fine for value_or(), but not for any number of other
>> operations that work for optional but not for pointers. What if you wanted
>> the size of the span there, or 0?
>>
>> With optional, these things just compose straightforwardly, so:
>> m.value_at(p).transform(ranges::size).value_or(0)
>>
>> How do you do that with a pointer?
>>
>> Note that neither of these examples work with
>> optional<reference_wrapper<T>> either.
>>
>
> It seems to me you do need a pointer, because they carry an intrinsic
> "optionality" semantics with themselves, yet bare pointers lack the
> functionalities std::optional has, such as the value_or() method and
> possibly others. As already stated one can't have std::optional<T&> and
> std::optional<std::reference_wrapper<T>> is a substitute of it.
>
> But I believe we can have the best of both worlds: we could wrap a pointer
> into a special type that std::optional would be specialized over, so that
> you can actually do things like
>
> m.value_at(p).value_or(...);
>
> As in the below example:
>
> int main() {
> test_class test("hello");
>
> std::cout << test.value_at("hello").value_or("not found") << std::endl;
> std::cout << test.value_at("unexisting").value_or("not found") <<
> std::endl;
>
> return 0;
> }
>
> Which would produce this output:
>
> hello
> not found
>
> I've built a working example on godbolt. Have a look:
> https://godbolt.org/z/6K7ahb3v4
>
> Regards,
> Fabio
>

The reason optional<T&> is a vastly superior solution to
optional<reference_wrapper<T>> is because:

1) Given a U, you want to be able to write optional<U>. The former lets you
do that, and the latter requires you to write
optional<some_metafunction<U>> - which is very tedious for producers.
2) optional<T&> holds a T&, but optional<reference_wrapper<T>> holds a
reference_wrapper<T> - those have very different API surfaces, the former
being much easier to use - unless you do more metaprogramming - which is
very tedious for consumers.

Your optional<pointer_wrapper<T>> does not address problem 1. But it
addresses problem 2 in an especially weird way that violates the semantics
that optional<U> holds a U (or not). You would have optional<U> holds a U,
except when U=pointer_wrapper<T> in which case it holds a T&? That makes it
tedious to use for consumers in a different way, because you're breaking
the semantics of optional. For instance, implementing map as a free
function taking an optional<U> and a function object F would typically
check is F is invocable<U&>, which is correct for optional as it exists
today and would still be correct for optional<T&>, but that would no longer
be correct for your specialization since you would end up with both false
positives and false negatives (since the type that you're constraint
checking with and the type that you're actually invoking the function with
differ).

Barry

Received on 2021-08-01 14:52:57