Boost logo

Boost :

Subject: Re: [boost] [outcome] How to drop the formal empty state
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2017-05-26 05:33:10


Le 26/05/2017 à 01:36, Gavin Lambert via Boost a écrit :
> On 26/05/2017 02:30, Niall Douglas wrote:
>> This is precisely why I added a formal empty state, and default
>> initialised to that. Because it causes behaviour different to valued or
>> errored, it **very** effectively traps logic errors during code
>> development. On **many** occasions it has successfully illuminated
>> poorly thought through code that I have written by bringing to my
>> attention - early and very obviously - that someone was very wrong. I am
>> absolutely convinced it is a great design choice.
>
> My worry with the formal empty state is precisely that as neither a
> value nor an error, someone might fail to check for it in code and
> then proceed under a false assumption. (If not has_error() then this
> void method must have succeeded, and I don't call value() because it
> was void.)
>
> Provided that .value() and .error() both throw if called in the empty
> state (which I assume is the case), that helps mitigate a large part
> of that, but not entirely, as in the case above.
std::experimentall:expected<T,E>::error, doesn't throws. I don't see a
use case where we want to retrieve an error without checking before.
Maybe you have a case.
>
> Yes, it's a misuse of the type, but it's one that I can see being very
> likely to happen in the real world. Even if the possibility of an
> empty state is heralded with bold blinking all-caps on all doc pages.
>
>> I'd therefore be happier with default construction giving uninitialised
>> contents, or a default constructed T or E. No overloading state of E.
>
> I strongly agree with Peter Dimov that introducing uninitialized state
> is a very undesirable thing. I still regard it as very rare in the
> majority of codebases that sanitizers that would detect that sort of
> thing would ever be run, so this would simply introduce the very type
> of UB that having a formal error-handling type is intended to reduce.
>
>
> I don't like the idea of a default-constructed T because T is not
> always default-constructible, and this makes it inconsistently behaved
> for different T and makes it harder to use uniformly in containers,
> especially in generic code.
>
> I don't like the idea of a default-constructed E because by convention
> (even if not quite in fact as Niall has pointed out -- though I've yet
> to see a platform where a 0 error code *didn't* mean success, other
> than cases where the formal type is int but is actually used as bool)
> the default-constructed error_code means "no error", and this is
> heavily reinforced by its operator bool semantics.
>
> I do like the idea of a non-default-constructed error code, because
> failure to initialise the result does seem like an error to me. Niall
> points out that this is harder to detect and treat specially in code
> but I don't agree with that; as long as a suitably unique error code
> is used then a simple assert in the error path would pick it up, no
> problem.
>
> If the consensus is that an initial non-default error code is not
> satisfactory, then a formal empty state seems to me like the least
> worst alternative. I just know that it's going to bite someone at
> some point.

If we don't provide a default constructor for expected<T> we could be
forced to use optional<expected<T>>.

This allows to don't pay for this empty state when we don't need it. The
problem is that we are paying more than needed when we need it.

We have two options:
* we specialize optional<expected<T,E>>
* we rename the intended specialization xxx<T> is similar to
optional<expected<T>>. xxx could be outcome::result or optional_expected

Vicente


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