Boost logo

Boost :

Subject: Re: [boost] [variant2] Need rationale for never-empty guarantee
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2019-03-01 12:44:19


pt., 1 mar 2019 o 11:57 Peter Dimov via Boost <boost_at_[hidden]>
napisał(a):

> Andrzej Krzemienski wrote:
>
> > But this is not the case for variant: both default-constructed state and
> > the moved-from state is not "valueless", even in std::variant. The only
> > way to get to a valueless state is to trigger an exception from a move
> > constructor.
>
> This is not true, by the way (although it should have been). std::variant
> also goes valueless on emplace when the construction throws. I have argued
> against this behavior in https://wg21.link/p0308r0, which also contains
> other interesting things. :-)
>

Thanks for the correction. Although it does not change the reasoning in
this thread: valueless state can only be observed during stack unwinding
(or when the unwinding was stopped too soon).

> (It was even less true before P0308 was partially adopted.)
>
> > And it seems to me that when this happens, the only reasonable choice
> for
> > the user is to either reset or destroy the variant.
>
> I can only repeat what I said about singular states. Stack unwinding in
> this
> case is an example of "world #2" code, which can observe a valueless
> variant. This means that it must be careful to never ever call an ordinary
> "world #1" function on a variant object.
>

This is technically true. I argue that an attempt to observe the states of
local objects, during stack unwinding is something wrong on itself: you do
not want to do this. Therefore being careful not to call ordinary "world
#1" function should come automatically from following other good practices:
do not fiddle with local objects during stack unwinding: just let them be
destroyed.

> There's really nothing variant-specific here; the same argument applies to
> any function having the basic exception safety guarantee. When you assign
> to
> a vector, and the assignment throws, you can only reasonably reset or
> destroy the vector. But we don't make it destroy-only.
>

Yes, when you use vec.assign(b, e); you may end up in an unintended state.
In that case you can only reasonably reset or destroy the vector.If you try
to count its elements, there is no UB, but at the same time you are doing
something wrong. What do you want to do with such count?

In case of a vector, in this state you can do more things with it. But this
is a guarantee that adds no value to anyone. This is at least what I am
trying to investigate with this thread.

And also note that if I wanted to get from state A to state B and due to an
exception I ended up in a valid but unspecified state, I cannot call just
any operations that were valid on A and B:

std::vector<X> v(10), w(10);
v[9]; // valid
w[9]; // valid
try{
v.assign(w.begin(), w.end());
}
catch(...)
{
  v[9]; // maybe UB
}

> And if you do have a type that is destroy-only, it breaks basic exception
> safety everywhere it's used. If you assign vector<variant> and it throws,
> the vector is now destroy-only.
>

No: it is normal that in "valid but unspecified state" you can do less than
other states:

E.g., I can call `*p = 0` when I know that pointer `p` points to an object
within its lifetime, but I cannot call this operation safely when a `p` is
in a "valid but unspecified state".

> > Yes: it makes a lot of sense to me to make an attempt to access the
> value
> > of a valueless variant an undefined behavior. I do not associate this
> > decision with the problems of types with singular states in general,
> > because there is no easy way to obtain the valueless state in variant;
>
> It doesn't have to be easy to be a problem. In fact, easy is better,
> because
> it happens more frequently and is therefore tested.
>

I would still want to hear from you or other people if they ever use a
variant (or in fact any other object with assignment that offers basic
exception safety guarantee) in this way:

An exception is thrown, but I stop the stack unwinding so that the object
is still in scope, and I do (intentionally) anything else than resetting it.

Does anyone do it?

Regards,
Andrzej


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