Le 30/05/2017 à 00:59, Gavin Lambert via Boost a écrit :
On 30/05/2017 03:07, Niall Douglas wrote:
Sorry, I probably should have said "since *I assume* outcome
is legal". I can imagine some scenarios where that might be useful (perhaps as part of some error translation code which wants to return a modified value but can also fail) but I suppose that would complicate the constructors a bit so I can understand why it'd be disallowed, and it should be rare enough not to really bite anyone.
(Although tagged constructors to resolve that ambiguity wouldn't be an issue if you could only create an errored outcome with a helper like make_unexpected...)
It's disallowed purely to avoid SFINAE on constructors and thus improve compile times.
That's why I mentioned tagged constructors. No SFINAE needed. Something like:
template<typename T> class result { public: struct error_tag {};
outcome(const T& val) { set_value(val); } outcome(error_code err, error_tag) { set_error(err); } //... };
template<typename T> result<T> make_error(error_code err) { return result<T>(err, result<T>::error_tag()); }
You could even make the error_tag constructor private and make_error a friend to ensure that make_error is the only possible way to produce an errored result. (Or keep the constructor public but make error_tag private; same effect.)
With this setup, there is no ambiguity when T is error_code -- user code with "return err" will return the error code as the value, and with "return make_error(err)" will return it as an error.
I'm not sure I would actually *recommend* this design, I'm just pointing out that it's possible and removes the ambiguity without any SFINAE. This is is someway what the Expected proposal proposes :) See of unexpect_t, make_unexpected and the make_expected_from_error factories.
Vicente Vicente