C++ Logo

std-proposals

Advanced search

Re: Yet another member function for std::map

From: Andrey Semashev <andrey.semashev_at_[hidden]>
Date: Sun, 1 Aug 2021 23:15:09 +0300
On 8/1/21 10:52 PM, Barry Revzin via Std-Proposals wrote:
>
>
> On Sun, Aug 1, 2021 at 2:41 AM Fabio Alemagna <falemagn_at_[hidden]
> <mailto:falemagn_at_[hidden]>> wrote:
>
>
>
> Il giorno gio 29 lug 2021 alle ore 02:30 Barry Revzin via
> Std-Proposals <std-proposals_at_[hidden]
> <mailto: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 <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.

3) optional<T&> can be optimized to store T* inside while
optional<reference_wrapper<T>> cannot.

> 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 15:15:17