Date: Mon, 2 Aug 2021 11:43:04 +0300
On 8/2/21 9:59 AM, Edward Catmur wrote:
> On Sun, 1 Aug 2021 at 21:15, Andrey Semashev via Std-Proposals
> <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>>
> wrote:
>
> 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]>
> > <mailto: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]>
> > <mailto: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>
> <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.
>
>
> Well, given that the same library provides both, actually it can - you'd
> just need to make optional<reference_wrapper<T>> a friend of
> reference_wrapper<T> so that it can put the latter into a hidden
> singular state.
This would be an ABI breaking change, which means it will never happen.
It also ties two unrelated components, and therefore not good from the
design standpoint.
> On Sun, 1 Aug 2021 at 21:15, Andrey Semashev via Std-Proposals
> <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>>
> wrote:
>
> 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]>
> > <mailto: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]>
> > <mailto: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>
> <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.
>
>
> Well, given that the same library provides both, actually it can - you'd
> just need to make optional<reference_wrapper<T>> a friend of
> reference_wrapper<T> so that it can put the latter into a hidden
> singular state.
This would be an ABI breaking change, which means it will never happen.
It also ties two unrelated components, and therefore not good from the
design standpoint.
Received on 2021-08-02 03:43:09