Subject: Re: [boost] [variant2] Andrzej's review -- design
From: Gavin Lambert (boost_at_[hidden])
Date: 2019-04-03 02:06:53
On 3/04/2019 13:38, Emil Dotchevski wrote:
> The problem with explicitly testing is that it must be done everywhere
> because we don't know if maybe we are being called from an error-handling
> context (note, this doesn't necessarily mean exception handling). For
> example, every member function must check if the class invariants are in
> place for *this (assuming UB is not acceptable).
How is this different if the instance can be empty, or can unexpectedly
change from type A to type B?
If you don't know whether you're handling an errored instance or not,
then it could be unexpectedly empty (with one variant implementation) or
it could be unexpectedly type B (with another implementation). I don't
see any practical difference between these cases.
> Under the basic guarantee, you'd only check if it contains an int if you
> care whether it contains an int. Typical use is not to check, but to do
> something if it contains an int and something else if it contains a
> different type. There is no check.
That's still a check, even if the check is inside of visit().
> It is not as bad. Introducing the empty state forces us to define behavior
> for that state. This is logically equivalent to requiring well defined
> behavior from any member function even for instances that failed to
> initialize. It's better to ensure and then assume that this can not happen.
The empty state is just another type that the variant can hold. Think
of it as std::monostate always implicitly being the first type argument
of the variant (as Peter points out).
My point is that I don't see any advantages to requiring that people
specify this explicitly, and I only see disadvantages to allowing people
to not specify it at all.
If it's illegal for the first type of the variant to be anything other
than std::monostate (which I think should be the case), then it could be
implicit rather than explicit. And both implementation and usage of the
variant would be dramatically simplified.
And then you also get the useful properties that variant<> is valid
(which is handy for generic variadic code) and variant<T> is equivalent
to optional<T> (and variant<T, U> is similar to optional<union<T, U>>,
but better). This just "makes sense" to me.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk