C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Dummy value protocol

From: Robin Savonen Söderholm <robinsavonensoderholm_at_[hidden]>
Date: Sat, 7 Jun 2025 20:44:02 +0200
You can easily implement your own version of optional that does exactly
that (it would even be a simpler class to implement, although not to use).
The question then is why? If you somehow think that you would gain
performance out of it, then my suggestion is: implement this type of
optional and show both a benchmark where it makes a significant difference
and tell us the use case where this performance gain can be leveraged.

My 2 c.

P.S. you may already "pay" for a branch in the case of std::string and the
like due to SSO, I'm sure the compiler can do something smart with the
extra branch in optional for both move and destruction.
D.S

// Robin

On Sat, Jun 7, 2025, 19:48 Avi Kivity via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Sat, 2025-06-07 at 19:15 +0200, Jan Schultke wrote:
> > > Why wouldn't it work with fundamental types?
> > > I explicitly marked them as supporting the protocol. Maybe I did a
> > > bad job of explaining myself.
> >
> > Because every possible value of type int is not an empty
> > std::optional
> > when you put it into std::optional already. You cannot define int(-1)
> > to be an empty std::optional after the fact, so you're stuck with
> > effectively storing an extra bool.
> >
> > This behavior can obviously not be changed at this point.
>
>
> But that is not my proposal. std::optional keeps its bool flag. The
> difference is in how the special member functions are generated:
>
>
> std::optional<T>::~optional() {
> if constexpr (uses the dummy protocol) {
> _value->~T();
> } else {
> if (_engaged) {
> _value->~T();
> }
> }
> }
>
> We avoided a conditional. For fundamental types, we didn't win
> anything, but (say) the move constructor becomes simpler:
>
>
> std::optional<T>::optional(optional&& o) {
> // omitting exception safety
> if constexpr (uses the dummy protocol) {
> _engaged = std::exchange(o._engaged, false);
> _value = std::move(o._value);
> } else {
> _engaged = std::exchange(o._engaged, false);
> if (_engaged) {
> // Can be replaced with std::relocate_at()
> std::construct_at(&_value, std::move(o._value));
> std::destroy_at(&o._value);
> }
> }
> }
>
> For fundamental types, and for trivially relocatable types, moving an
> optional becomes just data movement with no conditionals.
>
>
> > On Sat, 7 Jun 2025 at 19:12, Avi Kivity <avi_at_[hidden]> wrote:
> > >
> > > Why wouldn't it work with fundamental types? I explicitly marked
> > > them as supporting the protocol. Maybe I did a bad job of
> > > explaining myself.
> > >
> > > And again some raises std::expected, it's not related. This is for
> > > every container that can conditionally carry a value, std::optional
> > > is the best example but I encountered the problem while working on
> > > a different class (an interval type).
> > >
> > > On Sat, 2025-06-07 at 13:24 +0200, Jan Schultke wrote:
> > >
> > > I don't think it's worth the effort if it cannot work for
> > > fundamental
> > > types, and the ABI for std::optional is set in stone. Ideally, we
> > > would be able to say that std::optional<X*> where nullptr stores
> > > the
> > > empty value, or std::optional<int> where -1 represents an empty
> > > value,
> > > etc.
> > >
> > > Those things seem feasible with std::expected with a bit of library
> > > support. We could have something like
> > >
> > > std::expected<int, std::nonvalue<-1>>
> > >
> > >
> > > Which would use the value "-1" to represent an unexpected object.
> > > This
> > > would also not break any existing code since it would use novel
> > > types
> > > and a novel protocol, even in the case of fundamental types.
> > >
> > >
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2025-06-07 18:44:19