Date: Sun, 4 Oct 2020 18:20:40 -0300
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
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
Received on 2020-10-04 16:20:52