On Sun, Aug 1, 2021 at 2:41 AM Fabio Alemagna <falemagn@gmail.com> wrote:


Il giorno gio 29 lug 2021 alle ore 02:30 Barry Revzin via Std-Proposals <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

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