Boost logo

Boost :

Subject: Re: [boost] [variant2] Need rationale for never-empty guarantee
From: Peter Dimov (lists_at_[hidden])
Date: 2019-03-01 09:59:21


Andrzej Krzemienski wrote:
> So, does the following recommendation correctly capture the design goals
> for boost::variant2?
>
> If you require the never-empty guarantee (and accept the costs) use
> boost::variant2.
>
> If you do not require the never empty guarantee use std::variant.

Kind of, but as written this implies that std::variant has no costs, which
is not true. The checks for valueless do carry a cost. Each visit(), for
example, starts with `if(valueless) throw`, which is not necessary in
variant2.

> Also, I am not entirely satisfied with the reply, "those who want this
> guarantee". Could you, or anyone else, give me a real-world use case where
> a never-empty guarantee is needed, but a strong exception guarantee is
> not?

My reply was unsatisfactory because I was really not looking forward to
rehashing the arguments against singular states. Singular state is bad when
a result of two-phase construction (which is why we no longer use two-phase
construction), it's bad when a result of exception (which is why we don't
use destroy-only exception safety but basic exception safety), it's bad when
a result of default initialization of built-in types (but we can do nothing
about it), and it's bad when a result of a move (which is why move
semantics, as originally specified, do not put the moved-from object in a
singular state.)

Singular states introduce implicit "is_valid" preconditions on all your
normal functions, and partition the program into two worlds, a normal world
where no object is singular, and an "exceptional" world where objects may be
singular. It's _possible_ to program in this way, but it's not fun, because
world #2 may never call into world #1 under penalty of undefined behavior,
and singular objects are never to enter world #1, because this sets up a
delayed explosion.

If you avoid singular states, this removes all these implicit "is_valid"
preconditions, which removes the partitioning and collapses the two worlds
back into one; "type 2" code can call "type 1" code and nothing undefined
will happen.

Now in principle, for the specific case of move, it's possible and sound to
specify it to leave the object in a singular state, provided that you only
ever move from objects that are about to be immediately destroyed. But
that's not the approach that was taken. In this timeline, move does not
leave objects in a singular state, so there is no requirement to only ever
use it on objects that are about to be destroyed.

For variant specifically, the guarantee that variant<X, Y> can only ever
either hold an X, or hold a Y, simplifies the specification of all code
taking variant<X, Y>, because it's not required to document what happens in
the event of the variant not holding X or Y.

"Never empty" is somewhat a misnomer, because variant2 can be empty, you
just have to request it explicitly: variant<monostate, X, Y>. Of course then
you have to explicitly handle the possibility of the variant being empty. If
the variant is an implementation detail of some component of yours that has
behavior X' when in state X and behavior Y' when in state Y, you would need
to decide what happens when the variant is empty. Do you emulate X' or Y',
or does the component behave in a third way, E'? Up to you, but undefined
behavior is probably not acceptable, unless you introduce a singular state
for your component.

What are the alternatives? One is to do what std::variant does and try to
have the cake and eat it too. Have the empty state, but don't acknowledge it
in the interface as equal to other states, throw an exception instead. This
is a bit like sweeping the problem under the carpet, it allows people to
pretend that the variant delivers the "never empty" guarantee and program as
if it did, whereas it doesn't.

If you were serious about your convictions you would make accessing a
valueless variant undefined behavior, which avoids the valueless checks but
see above about singular states and time bombs.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk