2017-05-26 16:18 GMT+02:00 Niall Douglas via Boost
On 26/05/2017 07:41, Gavin Lambert via Boost wrote:
On 26/05/2017 17:33, Vicente J. Botet Escriba wrote:
std::experimentall:expected
::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. Perhaps unit tests, where you're expecting an error but the code unexpectedly succeeds.
Very true. I have a next gen unit testing infrastructure built around Outcomes where UB on any of the observers would be a no-go. You can see the idea of it at https://github.com/ned14/boost.afio/blob/master/test/ tests/file_handle_create_close/runner.cpp#L34 and note how self describing the tables of success and failure are.
At least in terms of storage, the current implementation of empty state is presumably free (it should be no more expensive to internally store a variant
than a variant ). And it's currently required to exist due to exception guarantees (and possible noexcept(false) move constructors). As mentioned in another discussion thread, the empty state is also used internally as a micro-optimisation. So it would likely remain internally whatever the decision taken here, as a tool for making the CPU expend identical CPU cycles on both positive and negative branches on state.
Again, if people don't like that behaviour of outcome/result to be equally costly on T or E branches chosen,
What does it mean tha "outcome is equally costly on T or E branches chosen"?
expected
empty state and therefore always naturally favours the E state (because in .value() you check for an errored state, and if so throw an exception, so returning a value is usually the branch the compiler generates a branch to later code for).
I don't think that T should be restricted to noexcept(true)-movable types only, as this prevents using it with C++03 non-POD types (that have a copy constructor but lack a move constructor), which are still likely to be widespread in codebases (although perhaps less common as return values).
I couldn't agree more about type T. But Expected does not demand nothrow move construction from type T, only type E. And usually most of the time the end user will control the source code for any type E used. It's a fair restriction in exchange for never empty.
Another consideration is that regardless of default construction or not is that you need to decide what an expected
will contain if someone moves-from it (directly). Is it now formal-empty or does it now contain a moved-from-T or moved-from-E? Or does it contain a moved-from-variant (if that's different)? We never change the state unless the user asks explicitly for that. So if they move from an expected
and the state was an E, it remains an E, just a moved-from E. Resetting to empty looks attractive, but I found out the hard way it is a bad design decision. Code consuming a rvalue reference does not actually have to move anything, nothing in the C++ standard says it does. It's only a widely held convention that it ought to.
In optional<T> we deliberately don't put the moved-from object into an empty state (which surprises many people). This is for performance reasons: when `T` is a trivial type, `optional<T>` can be made trivially_copyable.