Github: https://github.com/ned14/outcome
Docs (highly incomplete): https://ned14.github.io/outcome/
This is a welcome development, if still a bit too-policy-heavy for my taste.
The policy is there purely to say what to do when you call .value() on a non-valued result/outcome. Compelling use cases could be: - Delay constructing metadata to accompany any exception throws. - Use .error() as a key to look up an associative map. - Invoke Emil's Noexcept instead :) One could have used ADL customisation points instead, but those tend to surprise end users, so I felt the policy approach was better and leave the ADL points for advanced users.
Regarding construction, I have the following suggestion.
Instead of reusing in_place_type, use the following tag types:
struct in_place_value_t { constexpr in_place_value_t() noexcept {} };
constexpr in_place_value_t in_place_value;
struct in_place_error_t { constexpr in_place_error_t() noexcept {} };
constexpr in_place_error_t in_place_error;
This is isomorphic but superior to using in_place_index<0> and in_place_index<1>, which were my initial choice. Now the ambiguity when T == EC is resolved and
template
result( in_place_value_t, A&&... a ); always initializes the value and does not need to be disabled when T == EC.
You've struck on a very interesting design point indeed, and one which
caused me a few restless nights.
My initial approach was to do exactly what you just described, after all
it's how v1 implemented its tagged emplacing constructors, and v1 had a
host of make_(valued|errored|excepted)_*() functions too. v1's API was
all about construction of specific, named state.
But upon further reflection - and I want to emphasise that I think the
jury is still out on this decision, and I may revert during the next few
months - I decided that std::variant<> by-type emplacement was superior,
so I went with that instead.
The reason why is subtle. These types, result & outcome, are not in the
same use space as Expected where expected
In addition, I would add
template
result( A&&... a ); which initializes the value when it's constructible from a..., the error when that's constructible from a..., and is disabled when neither or both are constructible. (The implicit/explicit duality when sizeof...(A) == 1 complicates the actual implementation of the above but conceptually it still works as explained. Also, the sizeof...(A) == 0 case needs to be disabled.)
The reason I kept to single argument converting constructors is that I felt that the added gain of not having to type in_place_type<> was not worth the additional compile time load. Also variadic templates are slow :( And finally, std::variant doesn't implement multiple argument converting constructors. And I figured WG21 know what they're doing. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/