std::unexpected(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– NAME πŸ’–

Okay so this is std::unexpected(3), straight from the C++ Standard Libary manual, and the NAME section is literally:

  • std::unexpected - std::unexpected β€” yes bestie, the name is exactly that. No notes. πŸ’…

✨ Synopsis ✨

Defined in header <expected>

template< class E >           (since C++23)
class unexpected;

The class template std::unexpected is literally the holder for an unexpected value that lives inside std::expected. In particular, std::expected has constructors that take std::unexpected as a single argument, and those create an expected object containing an unexpected value. 🫢

A program is ill-formed if it instantiates an unexpected with a non-object type, an array type, a specialization of std::unexpected, or a cv-qualified type. Not cute, don't do it. πŸ’€


🧚 Template parameters 🧚

  • E β€” the type of the unexpected value. This type must not be an array type, a non-object type, a specialization of std::unexpected, or a cv-qualified type. Periodt.

πŸŽ€ Member functions πŸŽ€

  • constructor β€” constructs the unexpected object. (C++23) (public member function)
  • destructor (implicitly declared) β€” destroys the unexpected object, along with the stored value. (C++23) (public member function)
  • operator= (implicitly declared) β€” assigns the stored value. (C++23) (public member function)
  • error β€” accesses the stored value. (C++23) (public member function)
  • swap β€” swaps the stored value. (C++23) (public member function)

πŸ¦‹ Non-member functions πŸ¦‹

  • operator== β€” compares the stored value. (C++23) (function template)
  • swap(std::unexpected) β€” specializes the std::swap algorithm. (C++23) (function template)

πŸ’… std::unexpected::unexpected πŸ’…

constexpr unexpected( const unexpected& ) = default;
(1) (since C++23)
constexpr unexpected( unexpected&& ) = default;
(2) (since C++23)
template< class Err = E >
(3) (since C++23)
constexpr explicit unexpected( Err&& e );
template< class... Args >
(4) (since C++23)
constexpr explicit unexpected( std::in_place_t, Args&&... args );
template< class U, class... Args >

constexpr explicit unexpected( std::in_place_t,
(5) (since C++23)

                       std::initializer_list<U> il,
Args&&... args );

Constructs a std::unexpected object. πŸ’–

  • 1,2) β€” copy/move constructor. Copies or moves the stored value, respectively.
  • 3) β€” constructs the stored value, as if by direct-initializing a value of type E from std::forward<Err>(e).
    • This overload participates in overload resolution only if:
      • std::is_same_v<std::remove_cvref_t<Err>, unexpected> is false, and
      • std::is_same_v<std::remove_cvref_t<Err>, std::in_place_t> is false, and
      • std::is_constructible_v<E, Err> is true.
  • 4) β€” constructs the stored value, as if by direct-initializing a value of type E from the arguments std::forward<Args>(args)....
    • This overload participates in overload resolution only if std::is_constructible_v<E, Args...> is true.
  • 5) β€” constructs the stored value, as if by direct-initializing a value of type E from the arguments il, std::forward<Args>(args)....
    • This overload participates in overload resolution only if std::is_constructible_v<E, std::initializer_list<U>&, Args...> is true.

🌸 Parameters 🌸

  • e β€” value with which to initialize the contained value
  • args... β€” arguments with which to initialize the contained value
  • il β€” initializer list with which to initialize the contained value

😭 Exceptions 😭

Throws any exception thrown by the constructor of E. No extra drama, just whatever E's constructor throws.


πŸ”₯ std::unexpected::error πŸ”₯

constexpr const E& error() const& noexcept;

constexpr E& error() & noexcept;               (since C++23)
constexpr const E&& error() const&& noexcept;

constexpr E&& error() && noexcept;

Returns a reference to the stored value. That's it, that's the vibe. ✨


🌈 std::unexpected::swap 🌈

constexpr void swap( unexpected& other )
(since C++23)
noexcept(std::is_nothrow_swappable_v<E>);

Swaps the stored values, as if by using std::swap; swap(error(), other.error());.

The program is ill-formed if std::is_swappable_v<E> is false. Not negotiable, bestie. 🫠


⭐ operator==(std::unexpected) ⭐

(since C++23)

template< class E2 >
friend constexpr bool operator==( unexpected& x, std::unexpected<E2>&
y );

Compares the stored values, as if by return x.error() == y.error().

If the expression x.error() == e.error() is not well-formed, or if its result is not convertible to bool, the program is ill-formed. 😭

This function is not visible to ordinary unqualified or qualified lookup, and can only be found by argument-dependent lookup when std::unexpected<E> is an associated class of the arguments. iykyk.


🫢 swap(std::unexpected) 🫢

(since C++23)

friend constexpr void
swap( unexpected& x, unexpected& y ) noexcept(noexcept(x.swap(y)));

Equivalent to x.swap(y).

This overload participates in overload resolution only if std::is_swappable_v<E> is true.

This function is not visible to ordinary unqualified or qualified lookup, and can only be found by argument-dependent lookup when std::unexpected<E> is an associated class of the arguments. Same ADL rules, same main character energy. πŸ’…


πŸŽ€ Deduction guides πŸŽ€

template< class E >              (since C++23)
unexpected(E) -> unexpected<E>;

The deduction guide is provided for unexpected to allow deduction from the constructor argument. Slay. ⭐


πŸ’œ Notes πŸ’œ

Prior to C++17, the name std::unexpected denoted the function called by the C++ runtime when a dynamic exception specification was violated. History tea, no cap. 🍡


πŸ“ Example πŸ“

// Run this code

#include <expected>
#include <iostream>

int main()
{
    std::expected<double, int> ex = std::unexpected(3);

    if (!ex)
        std::cout << "ex contains an error value\n";

    if (ex == std::unexpected(3))
        std::cout << "The error value is equal to 3\n";
}

✨ Output ✨

ex contains an error value
The error value is equal to 3

🧚 Example with enum 🧚

// Run this code

#include <expected>
#include <iostream>

enum class error
{
    compile_time_error,
    runtime_error
};

[[nodiscard]] auto unexpected_runtime_error() -> std::expected<int, error>
{
    return std::unexpected(error::runtime_error);
}

int main()
{
    const auto e = unexpected_runtime_error();

    e.and_then([](const auto& e) -> std::expected<int, error> {
         std::cout << "and_then: " << int(e); // not printed
         return {};
     }).or_else([](const auto& e) -> std::expected<int, error> {
        std::cout << "or_else: " << int(e); // prints this line
        return {};
    });

    return 0;
}

πŸ“ Output πŸ“

or_else: 1

πŸ‘‘ See also πŸ‘‘

  • constructor β€” constructs the expected object. (public member function)
  • operator== β€” compares expected objects. (C++23) (function template)

Okay bestie, source/version/date vibes: this page is from http://cppreference.com, dated 2024.06.10, and it's literally the std::unexpected(3) manual page. Mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from