omni::expected

Defined in omni/Expected.h

template<typename TValue, typename TError>
class expected : public omni::detail::ExpectedImpl<TValue, TError>

A monad which holds either an expected value (the success case) or an unexpected value (the error case).

Simple use of expected instances involve checking if an instance has_value() before accessing either the value() or error() member.

expected<int, string> foo();

int main()
{
    auto ex = foo();
    if (ex)
        std::cout << "Successfully got " << ex.value() << std::endl;
    else
        std::cout << "Error: " << ex.error() << std::endl;
}

Advanced usage of expected involves using the monadic operations, which act on the stored value. This example is equivalent to the above:

expected<int, string> foo();

int main()
{
    foo()
        .transform([](int value) { std::cout << "Successfully got " << value << std::endl; })
        .transform_error([](string const& err) { std::cout << "Error: " << err << std::endl; });
}

Template Parameters
  • TValue – The type of expected value, returned in the success case.

  • TError – The type of unexpected value, returned in the error case.

Public Types

using value_type = TValue

The type used in success case.

using error_type = TError

The type used in error cases. Unlike the C++23 definition of std::expected, this is allowed to be void to match parity with other languages with result monads.

using unexpected_type = unexpected<error_type>

The unexpected type which contains this monad’s error_type.

template<typename UValue>
using rebind = expected<UValue, error_type>

Get an expected type with UValue as the value_type and the same error_type as this.

Public Functions

expected() = default

Create a valued instance through default construction.

This constructor is only enabled if value_type is default-constructable or void.

This function is noexcept if value_type has a noexcept default constructor or if it is void.

expected(expected const &src) = default

Copy an expected instance from another. After this call, this->has_value() == src.has_value() and either the value or error will have been constructed from the src instance.

This operation is only enabled if both value_type and error_type are copy-constructible or void. This operation is trivial if both value_type and error_type have trivial copy constructors or are void.

This function is noexcept if both value_type and error_type have noexcept copy constructors or are void.

Parameters

src – The source to copy from. It will remain unchanged by this operation.

expected(expected &&src) = default

Move an expected instance from another. After this call, this->has_value() == src.has_value() and either the value or error will have been constructed from std::move(src).value() or std::move(src).error(). Note that the has_value state is unchanged, but the src instance will be moved-from.

This operation is only enabled if both value_type and error_type are move-constructible or void. This operation is trivial if both value_type and error_type have trivial move constructors or are void.

This function is noexcept if both value_type and error_type have noexcept move constructors or are void.

Parameters

src – The source to move from. While the has_value state will remain unchanged, the active src instance will have been moved-from.

expected &operator=(expected const &src) = default

Copy-assign this instance from another. After this call, this->has_value() == src.has_value().

Assignment can happen in one of two ways. In the simple case, this->has_value() == src.has_value() before the call, so the copy assignment operator of the underlying type is used. If this->has_value() != src.has_value() before the call, then the active instance of this gets the destructor called, then the other type is copy constructed (generally &#8212; see the “exceptions” section for more info). Note that this destruct-then-construct process happens even when value_type and error_type are the same.

This operation is only enabled if both value_type and error_type are copy-assignable or void. This operation is trivial if both value_type and error_type have trivial copy assignment operators and trivial destructors or are void.

This function is noexcept if both value_type and error_type have noexcept copy constructors, copy assignment operators, and noexcept destructors or are void.

Note

On type-changing assignment with exceptions enabled, care is taken to ensure the contents of the monad are valid for use when exceptions are thrown. The “simple” destruct-then-construct process is only followed when copy construction of the type of the created instance is non-throwing. The exact algorithm used depends on the available noexcept operations (if any), but they involve a stack-based temporary and rollback. Note that a new instance might be constructed before the destruction of the old, so the ordering of these operations should not be relied on.

Parameters

src – The source to copy from. It will remain unchanged by this operation.

expected &operator=(expected &&src) = default

Move-assign this instance from another. After this call, this->has_value() == src.has_value().

Assignment can happen in one of two ways. In the simple case, this->has_value() == src.has_value() before the call, so the move assignment operator of the underlying type is used. If this->has_value() != src.has_value() before the call, then the active instance of this gets the destructor called, then the other type is move constructed (generally &#8212; see the “exceptions” section for more info). Note that this destruct-then-construct process happens even when value_type and error_type are the same.

This operation is only enabled if both value_type and error_type are move-assignable or void. This operation is trivial if both value_type and error_type have trivial move assignment operators and trivial destructors or are void.

This function is noexcept if both value_type and error_type have noexcept move constructors, move assignment operators, and noexcept destructors or are void.

Note

On type-changing assignment with exceptions enabled, care is taken to ensure the contents of the monad are valid for use when exceptions are thrown. The “simple” destruct-then-construct process is only followed when move construction of the type of the created instance is non-throwing. The exact algorithm used depends on the available noexcept operations (if any), but they involve a stack-based temporary and rollback. Note that a new instance might be constructed before the destruction of the old, so the ordering of these operations should not be relied on.

Parameters

src – The source to move from. While the has_value will remain unchanged, the active src instance will have been moved-from.

~expected() = default

Destroy this instance by calling the destructor of the active value.

This operation is trivial if both value_type and error_type are trivially destructible. This function is noexcept if both value_type and error_type have noexcept destructors.

template<typename UValue = TValue, std::enable_if_t<omni::detail::IsExpectedDirectConstructibleFrom<UValue, expected>::is_explicit, bool> = true>
inline explicit constexpr expected(UValue &&src)

Construct an instance by forwarding src to construct the value_type by direct initialization. After this call, this->has_value() will be true.

This constructor is only enabled when all of the following criteria is met:

  1. value_type is constructible from UValue

  2. value_type is not void

  3. remove_cvref_t<UValue> is not in_place_t

  4. remove_cvref_t<UValue> is not expected<TValue, TError> (the copy or move constructor is used instead)

  5. remove_cvref_t<UValue> is not a specialization of unexpected

  6. if value_type is bool, then UValue can not be a specialization of expected

This constructor is explicit if conversion from UValue to value_type is explicit.

template<typename UValue, typename UError, std::enable_if_t<omni::detail::IsExpectedCopyConstructibleFrom<expected<UValue, UError>, expected>::is_explicit, bool> = true>
inline explicit constexpr expected(expected<UValue, UError> const &src)

Convert from src by direct initialization from the active element. If src.has_value(), then this instance will have a value constructed from src.value(); otherwise, this instance will have an error constructed from src.error(). After this call, this->has_value() == src.has_value().

This converting constructor is not explicit if conversion from UValue to TValue and UError to TError are not explicit. Conversion from void to void is also considered a non explicit conversion. Stated differently, a UExpected is implicitly convertible to a TExpected if both of its components are implicitly convertible.

Note

The rules for this are almost identical to std::expected, but they are expanded to support void as the error_type. Any case where the C++ Standard makes an exception when value_type is void, that same exception has been extended to error_type of void.

template<typename UValue, typename UError, std::enable_if_t<omni::detail::IsExpectedMoveConstructibleFrom<expected<UValue, UError>, expected>::is_explicit, bool> = true>
inline explicit constexpr expected(expected<UValue, UError> &&src)

Convert from src by direct initialization from the active element. If src.has_value(), then this instance will have a value constructed from move(src).value(); otherwise, this instance will have an error constructed from move(src).error(). After this call, this->has_value() == src.has_value(). Note that the contents of src are moved-from, but not destructed, so the instances is still accessable.

This converting constructor is not explicit if conversion from UValue to TValue and UError to TError are not explicit. Conversion from void to void is also considered a non explicit conversion. Stated differently, a UExpected is implicitly convertible to a TExpected if both of its components are implicitly convertible.

Note

The rules for this are almost identical to std::expected, but they are expanded to support void as the error_type. Any case where the C++ Standard makes an exception when value_type is void, that same exception has been extended to error_type of void.

template<typename UError, std::enable_if_t<carb::cpp::conjunction<std::is_constructible<TError, UError const&>, carb::cpp::negation<std::is_convertible<UError const&, TError>>>::value, bool> = true>
inline explicit constexpr expected(unexpected<UError> const &src)

Construct an instance using src as the error value. The constructed instance !this->has_value() and the this->error() will have been constructed by src.error().

This constructor is not explicit if the conversion from the source UError to TError is not explicit. Stated differently, an unexpected<UError> is implicitly convertible to a expected<TValue, TError> (of arbitrary TValue) if UError is implicitly convertible to a TError.

If TError is void, then UError must also be void to construct an instance.

template<typename UError, std::enable_if_t<carb::cpp::conjunction<std::is_constructible<TError, UError&&>, carb::cpp::negation<std::is_convertible<UError&&, TError>>>::value, bool> = true>
inline explicit constexpr expected(unexpected<UError> &&src)

Construct an instance using src as the error value. The constructed instance !this->has_value() and the this->error() will have been constructed by std::move(src).error().

This constructor is not explicit if the conversion from the source UError to TError is not explicit. Stated differently, an unexpected<UError> is implicitly convertible to a expected<TValue, TError> (of arbitrary TValue) if UError is implicitly convertible to a TError.

If TError is void, then UError must also be void to construct an instance.

inline constexpr bool has_value() const noexcept

Test if this instance has a value. If this returns true, then a call to value() will succeed, while a call to error() would not. If this returns false, a call to error() will succeed, while a call to value() would not.

inline explicit constexpr operator bool() const noexcept

Test if this instance has a value. If this returns true, then a call to value() will succeed, while a call to error() would not. If this returns false, a call to error() will succeed, while a call to value() would not.

See also

has_value

constexpr value_type &value() &

If this instance has_value(), the value is returned by &.

If this instance does not have a value, this call will not succeed. If exceptions are enabled, then a bad_expected_access exception is thrown containing the copied contents of error(). If exceptions are disabled, then the program will terminate.

Note

If value_type is void, the return type is exactly void instead of void& (which is illegal).

constexpr value_type const &value() const &

If this instance has_value(), the value is returned by const&.

If this instance does not have a value, this call will not succeed. If exceptions are enabled, then a bad_expected_access exception is thrown containing the copied contents of error(). If exceptions are disabled, then the program will terminate.

Note

If value_type is void, the return type is exactly void instead of void const& (which is illegal).

constexpr value_type &&value() &&

If this instance has_value(), the value is returned by &&.

If this instance does not have a value, this call will not succeed. If exceptions are enabled, then a bad_expected_access exception is thrown containing the moved contents of error(). If exceptions are disabled, then the program will terminate.

Note

If value_type is void, the return type is exactly void instead of void&& (which is illegal).

constexpr value_type const &&value() const &&

If this instance has_value(), the value is returned by const&&.

If this instance does not have a value, this call will not succeed. If exceptions are enabled, then a bad_expected_access exception is thrown containing the copied contents of error(). If exceptions are disabled, then the program will terminate.

Note

If value_type is void, the return type is exactly void instead of void const&& (which is illegal).

template<typename UValue>
constexpr value_type value_or(UValue &&default_value) const &

If this instance has_value(), the value is copied and returned; otherwise, a value_type instance is constructed from default_value.

Note

If value_type is void, this member function does not exist. If value_type is not copy constructible, this will fail to compile in the immediate context (not SFINAE-safe).

Template Parameters

UValue – Must be convertible to value_type.

template<typename UValue>
constexpr value_type value_or(UValue &&default_value) &&

If this instance has_value(), the value is moved and returned; otherwise, a value_type instance is constructed from default_value.

Note

If value_type is void, this member function does not exist. If value_type is not move constructible, this will fail to compile in the immediate context (not SFINAE-safe).

Template Parameters

UValue – Must be convertible to value_type.

constexpr error_type &error() & noexcept

If this instance !has_value(), the error is returned by &.

Note

If error_type is void, the return type is exactly void instead of void& (which is illegal).

Pre

!this->has_value(): if this instance is not in the unexpected state, the program will terminate.

constexpr error_type const &error() const & noexcept

If this instance !has_value(), the error is returned by const&.

Note

If error_type is void, the return type is exactly void instead of void const& (which is illegal).

Pre

!this->has_value(): if this instance is not in the unexpected state, the program will terminate.

constexpr error_type &&error() && noexcept

If this instance !has_value(), the error is returned by &&.

Note

If error_type is void, the return type is exactly void instead of void&& (which is illegal).

Pre

!this->has_value(): if this instance is not in the unexpected state, the program will terminate.

constexpr error_type const &&error() const && noexcept

If this instance !has_value(), the error is returned by const&&.

Note

If error_type is void, the return type is exactly void instead of void const&& (which is illegal).

Pre

!this->has_value(): if this instance is not in the unexpected state, the program will terminate.

constexpr value_type *operator->() noexcept

Access the underlying value instance. If has_value() is false, the program will terminate.

This function is only available if value_type is not void.

constexpr value_type const *operator->() const noexcept

Access the underlying value instance. If has_value() is false, the program will terminate.

This function is only available if value_type is not void.

constexpr value_type &operator*() & noexcept

If this instance has_value(), the value is returned by &.

If this instance does not have a value, the program will terminate.

Note

If value_type is void, this overload is not enabled (only the const& is accessible).

constexpr value_type const &operator*() const & noexcept

If this instance has_value(), the value is returned by const&.

If this instance does not have a value, the program will terminate.

Note

If value_type is void, the return type is exactly void instead of void const& (which is illegal).

constexpr value_type &&operator*() && noexcept

If this instance has_value(), the value is returned by &&.

If this instance does not have a value, the program will terminate.

Note

If value_type is void, this overload is not enabled (only the const& is accessible).

constexpr value_type const &&operator*() const && noexcept

If this instance has_value(), the value is returned by const&&.

If this instance does not have a value, the program will terminate.

Note

If value_type is void, this overload is not enabled (only the const& is accessible).

template<typename ...TArgs>
value_type &emplace(TArgs&&... args) noexcept

Destroy the current contents of this instance and construct the value_type of this instance through direct-initialization.

If value_type is not void, this function accepts two overloads:

  1. template <typename... TArgs> value_type& emplace(TArgs&&... args) noexcept (only enabled if std::is_nothrow_constructible<value_type, TArgs...>::value is true)

  2. template <typename U, typename... TArgs> value_type& emplace(std::initializer_list<U>& il, TArgs&&... args) noexcept (only enabled if std::is_nothrow_constructible<value_type, std::initializer_list<U>&, TArgs...>::value is true)

After calling this function, has_value() will return true.

void emplace() noexcept

If value_type is void, then emplace is a no argument function that returns void.

After calling this function, has_value() will return true.

template<typename F>
inline constexpr auto transform(F &&f) const &

Transform the value by f if this has_value() or return the error if it does not.

Template Parameters

F – If value_type is not void, F has the signature UValue (value_type const&); if value_type is void, F has the signature UValue ().

Returns

An expected<UValue, error_type>, where the returned value has been transformed by f. The value_type of the returned instance is the result type of F.

template<typename F>
inline constexpr auto transform(F &&f) &

Transform the value by f if this has_value() or return the error if it does not.

Template Parameters

F – If value_type is not void, F has the signature UValue (value_type&); if value_type is void, F has the signature UValue ().

Returns

An expected<UValue, error_type>, where the returned value has been transformed by f. The value_type of the returned instance is the result type of F.

template<typename F>
inline constexpr auto transform(F &&f) &&

Transform the value by f if this has_value() or return the error if it does not.

Template Parameters

F – If value_type is not void, F has the signature UValue (value_type&&); if value_type is void, F has the signature UValue ().

Returns

An expected<UValue, error_type>, where the returned value has been transformed by f. The value_type of the returned instance is the result type of F.

template<typename F>
inline constexpr auto transform(F &&f) const &&

Transform the value by f if this has_value() or return the error if it does not.

Template Parameters

F – If value_type is not void, F has the signature UValue (value_type const&&); if value_type is void, F has the signature UValue ().

Returns

An expected<UValue, error_type>, where the returned value has been transformed by f. The value_type of the returned instance is the result type of F.

template<typename F>
inline constexpr auto and_then(F &&f) const &

Transform the value by f if this has_value() or return the error if it does not.

Template Parameters

F – If value_type is not void, F has the signature expected<UValue, UError> (value_type const&); if value_type is void, F has the signature expected<UValue, UError> (). In both cases, UError must be constructible from error_type or void.

template<typename F>
inline constexpr auto and_then(F &&f) &

Transform the value by f if this has_value() or return the error if it does not.

Template Parameters

F – If value_type is not void, F has the signature expected<UValue, UError> (value_type&); if value_type is void, F has the signature expected<UValue, UError> (). In both cases, UError must be constructible from error_type or void.

template<typename F>
inline constexpr auto and_then(F &&f) &&

Transform the value by f if this has_value() or return the error if it does not.

Template Parameters

F – If value_type is not void, F has the signature expected<UValue, UError> (value_type&&); if value_type is void, F has the signature expected<UValue, UError> (). In both cases, UError must be constructible from error_type or void.

template<typename F>
inline constexpr auto and_then(F &&f) const &&

Transform the value by f if this has_value() or return the error if it does not.

Template Parameters

F – If value_type is not void, F has the signature expected<UValue, UError> (value_type const&&); if value_type is void, F has the signature expected<UValue, UError> (). In both cases, UError must be constructible from error_type or void.

template<typename F>
inline constexpr auto transform_error(F &&f) const &

Transform the error by f if this has_value() is false or return the value if it does not.

Template Parameters

F – If error_type is not void, F has the signature UError (error_type const&); if error_type is void, F has the signature UError ().

Returns

An expected<value_type, UError>, where the returned error has been transformed by f. The error_type of the returned instance is the result type of F.

template<typename F>
inline constexpr auto transform_error(F &&f) &

Transform the error by f if this has_value() is false or return the value if it does not.

Template Parameters

F – If error_type is not void, F has the signature UError (error_type&); if error_type is void, F has the signature UError ().

Returns

An expected<value_type, UError>, where the returned error has been transformed by f. The error_type of the returned instance is the result type of F.

template<typename F>
inline constexpr auto transform_error(F &&f) &&

Transform the error by f if this has_value() is false or return the value if it does not.

Template Parameters

F – If error_type is not void, F has the signature UError (error_type&&); if error_type is void, F has the signature UError ().

Returns

An expected<value_type, UError>, where the returned error has been transformed by f. The error_type of the returned instance is the result type of F.

template<typename F>
inline constexpr auto transform_error(F &&f) const &&

Transform the error by f if this has_value() is false or return the value if it does not.

Template Parameters

F – If error_type is not void, F has the signature UError (error_type const&&); if error_type is void, F has the signature UError ().

Returns

An expected<value_type, UError>, where the returned error has been transformed by f. The error_type of the returned instance is the result type of F.

template<typename F>
inline constexpr auto or_else(F &&f) const &

Transform the error by f if this has_value() is false or return the value if it does not.

Template Parameters

F – If error_type is not void, F has the signature expected<UValue, UError> (error_type const&); if error_type is void, F has the signature expected<UValue, UError> (). In both cases, UValue must be constructible from value_type or void.

template<typename F>
inline constexpr auto or_else(F &&f) &

Transform the error by f if this has_value() is false or return the value if it does not.

Template Parameters

F – If error_type is not void, F has the signature expected<UValue, UError> (error_type&); if error_type is void, F has the signature expected<UValue, UError> (). In both cases, UValue must be constructible from value_type or void.

template<typename F>
inline constexpr auto or_else(F &&f) &&

Transform the error by f if this has_value() is false or return the value if it does not.

Template Parameters

F – If error_type is not void, F has the signature expected<UValue, UError> (error_type&&); if error_type is void, F has the signature expected<UValue, UError> (). In both cases, UValue must be constructible from value_type or void.

template<typename F>
inline constexpr auto or_else(F &&f) const &&

Transform the error by f if this has_value() is false or return the value if it does not.

Template Parameters

F – If error_type is not void, F has the signature expected<UValue, UError> (error_type const&&); if error_type is void, F has the signature expected<UValue, UError> (). In both cases, UValue must be constructible from value_type or void.