Hi Everyone. This is my first time posting to this group, so please be gentle. :-)
I'm in need of a value-or-error mechanism for a C++11 library I'm working on, where I don't want to impose exceptions to the client. Having completed a toy "result<T, E>" implementation inspired by std::expected, I decided to implement a complete C++11-ized version of the P0323R9 proposal.
I am questioning the rationale of allowing void as a value type for std::expected. As I understand it, expected is supposed to be a "vocabulary type" similar to std::optional, or std::variant, yet the latter two don't permit void as one of their contained types (as far as I know).
The spec for std::expected is currently very complex with constraints on overloads. expected(const expected<U, G>& rhs) in particular is quite scary and makes me wonder if it can slow down compilations times. Not having to worry about the T=void case would simplify things.
If a user wants to use std::expected to indicate a success-or-failure result (with no value), they could simply use something like std::monostate as the value type. What I did in my toy result<T,E> implementation is provide an empty success_t tag type that can be used when there is no meaningful value to return. For example:
struct success_t
{
constexpr success_t() = default;
constexpr bool operator==(success_t) const {return true;}
constexpr bool operator!=(success_t) const {return false;}
};
inline constexpr success_t successful;
result<success_t, std::error_code> write(const std::string& str)
{
if (writeToDeviceWorks(str))
return successful;
else
return with_failure(make_error_code(std::errc::device_or_resource_busy));
// with_failure is my equivalent of unexpected(), where I don't have the
// luxury of CTAD in C++11.
}
int main()
{
auto res = write("foo");
if (res == successful)
std::cout << "Yay!\n";
else
std::cerr << "Oh no: " << res.error() << "\n";
}
The "successful" object has the benefit that it's quite readable when used on the RHS of operator==, as shown above.
The one benefit of allowing T=void I can see is that a std::expected<void, E> specialization can get rid of its union storage and directly store error_type. Is this the rationale for allowing std::expected<void, E>?
Cheers,
Emile Cormier
--