Subject: Re: [boost] [variant2] Need rationale for never-empty guarantee
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2019-03-01 16:41:29
>> I would disagree with this assessment of the strong never empty
>> guarantee provided by variant2.
> Or do you disagree with my judgement that the never empty guarantee is not
> of much use?
> I agree that this is how you could observe the valueless state. But I also
> claim that such code (I wait to be convinced otherwise) has already more
> serious problems than observing the valueless state on variant. You
> typically do not want your objects to outlive the stack unwinding. And if
> you do, for globals, you want to provide a transactional-like guarantee.
> Leaving such objects in "valid but unspecified states" is a design bug.
My issue with std::variant is that it needlessly does not conform to the
usual assignment and emplacement guarantees of other standard library types.
For example, resizing a std::vector implements the strong guarantee that
state will be restored to before the resize if an exception is thrown in
the middle of the resize.
Boost.Optional implements the strong guarantee for assignment, but not
for emplacement (which I think it should, but fair enough that
emplacement generally has the basic guarantee. You might note that
Outcome deliberately omits an emplacement modifier). See
Thus I would hold that, unless there is a *very* good reason why not, so
should std::variant propagate the strong guarantee where it is able to
do so. To my knowledge, there is no good reason that is does not, as
proven by Peter's variant2.
> Or am I wrong?
I suppose it depends on whether you consider variant a sort of container
with responsibilities or not. I would say it is.
> My position (until I see examples that will convince me otherwise) is that
> if a programmer observes the valueless state in a variant, then the
> programmer is doing something wrong with handling exceptions.
My issue is that it is a point of unintended failure that need not exist.
The committee decided that exception throws during assignment or
emplacement ought to create a trap state which renders the variant
always throwing exceptions on use thereafter.
I can see the logic, but it is wrong in my opinion. The variant should
be put back into the state it was in beforehand, in my opinion.
(Personally speaking, I find the double buffering a step too far. I
remember debating this with Anthony Williams a few years ago at ACCU. I
think that if double buffering is necessary, then you weaken your
guarantees to basic, and you provide a constexpr bool for static
asserting when the guarantees are basic or strong. In any case, I find
the valueless by exception state to be an abomination, it should never
have been allowed, it litters the code with potential throw paths none
of which aid codegen)
> You are contrasting boost::variant2 with std::variant, but the design space
> I see is more than just either of them. If std::variant is wrong (which I
> tend to agree with) it does not immediately imply that boost::variant2is
> right. Another alternative that Peter indirectly suggested is that it is UB
> if you try to observe the valuelsess state in std::variant. In this case
> some usages after a throw are banned, but the model still guarantees the
> never emptiness.
You're right that it doesn't mean variant2 is right. And I do have some
issues with it as it is currently, which I have already covered in
enough detail here.
I feel far more strongly about propagation of triviality than I do about
double buffering. I only have a weakly held disagreement with double
buffering. It stems mainly from my belief that the compile time extra
cost is not worth it for supporting pathological types (i.e. ones
without noexcept move constructors). But that's a belief, not a fact, I
have no empirical evidence to prove my belief.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk