Hi,

4) optional<T&> implies the existence of optional<T&&>. Given that operator* already preserves value category for optional<T>, this could be really convenient in generic context. Not unlike tuple and get/apply.

The Achilles' heel of optional<T&> seems to be assignment. I'm of the opinion that it simply shouldn't have assignment from arguments of type T, but I believe there is no perfect answer.

-Lénárd


From: Andrey Semashev via Std-Proposals <std-proposals@lists.isocpp.org>
Sent: August 1, 2021 9:15:09 PM GMT+01:00
To: std-proposals@lists.isocpp.org
Cc: Andrey Semashev <andrey.semashev@gmail.com>
Subject: Re: [std-proposals] Yet another member function for std::map

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@gmail.com
<mailto:falemagn@gmail.com>> wrote:



Il giorno gio 29 lug 2021 alle ore 02:30 Barry Revzin via
Std-Proposals <std-proposals@lists.isocpp.org
<mailto:std-proposals@lists.isocpp.org>> 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




--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals