Реализация std::expected с одним заголовком с использованием C++20.
Библиотека реализует эти 2 предложения:
Эта реализация обеспечивает ожидаемое в пространстве имен rd. Итак, для любого использования std::expected используйте rd::expected.
std::expected — это механизм обработки ошибок. Это очень похоже на std::result в Rust. std::expected в основном содержит один из результатов или ошибок.
Вероятно, есть две причины не «всегда» использовать исключения:
При написании любой библиотеки это создает большую проблему с обработкой ошибок. В библиотеке, если мы решаем генерировать исключение в случае ошибки, мы делаем выбор за пользователя библиотеки. Состояние ошибки в библиотеке может быть или не быть исключительной ситуацией в контексте приложения, использующего эту библиотеку.
std::expected предоставляет стандартную альтернативу исключению, которую можно использовать для условий ошибки, которые не являются исключительными в контексте приложения.
При этом следует отметить, что исключения отлично подходят для панических состояний. И существует множество очевидных панических состояний, для которых следует использовать только исключения.
error_code раньше был простейшим механизмом обработки ошибок. Исторически error_code представляет собой простое целое число, которое не подходит для расширенного контекста ошибок.
В настоящее время заголовок C++ <system_error> предоставляет более богатый код ошибки. Однако error_code приводит к монополизации обратного канала. И поэтому значение необходимо вывести через какой-то другой канал.
Это также заставляет вас писать условие if после каждого вызова функции.
std::expected гарантирует, что он никогда не будет пустым. т. е. в любой момент времени оно будет содержать либо значение, либо ошибку. Это делает его действительно простым в использовании.
auto get_file_handle (std::string_view path) -> rd::expected<file, fail_reason>;
auto read_file (file f) -> rd::expected<std::string, fail_reason>;
auto to_int (std::string str) -> rd::expected<int, fail_reason>;
auto increment ( int x) -> int;
auto read_int_with_increment (std::string_view path) -> rd::expected<int, fail_reason>{
return get_file_handle (path)
. and_then (read_file)
. and_then (to_int)
. transform (increment);
}
template < typename T, typename E>
class expected
T, E не могут быть ссылочным типом, поскольку они также не поддерживаются в документе.
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template < class U >
using rebind = expected<U, error_type>;
Конструктор по умолчанию:
constexpr expected ();
Копировать конструктор:
constexpr expected (expected const &);
Переместить конструктор:
constexpr expected (expected &&);
Ожидаемый конструктор:
U должен быть конвертируем в T, а G должен быть конвертируем в E.
template < class U , class G >
constexpr expected (expected<U, G> const &);
template < class U , class G >
constexpr expected (expected<U, G> &&);
Конструктор значений:
Конструктор для ожидаемого <T , E>, который принимает значение для инициализации T (часть значения ожидаемого).
template < class U = T>
constexpr expected (U&& v);
Неожиданный конструктор:
Конструктор для ожидаемого<T, E>, который принимает экземпляр rd::unexpected для инициализации E (часть ожидаемой ошибки).
template < class G >
constexpr expected (unexpected<G> const &);
template < class G >
constexpr expected (unexpected<G> &&);
конструктор in_place_t:
Конструктор для ожидаемого<T, E>, который принимает тег in_place и аргументы (для пересылки) для инициализации T.
template < class ... Args>
constexpr expected (std:: in_place_t , Args&&... args);
template < class U , class ... Args>
constexpr expected (std:: in_place_t , std::initializer_list<U>, Args&&... args);
конструктор unexpect_t:
Конструктор для ожидаемого<T, E>, который принимает тег rd::unexpect и аргументы (для пересылки) для инициализации E.
template < class ... Args>
constexpr expected (rd:: unexpect_t , Args&&... args);
template < class G , class ... Args>
constexpr expected (rd:: unexpect_t , std::initializer_list<G>, Args&&... args);
задание копирования:
constexpr expected& operator =(expected const &);
переместить задание:
constexpr expected& operator =(expected &&);
Присвоение значения:
Присваивает значение ожидаемому
template < class U = T>
constexpr expected& operator =(U&& rhs);
rd::неожиданное назначение:
Присваивает ошибку ожидаемому
template < class G >
constexpr expected& operator =(unexpected<G> const &);
template < class G >
constexpr expected& operator =(unexpected<G> &&);
разместить:
Принимает аргументы для конструктора нового ожидаемого значения и присваивает ему.
template < class ... Args>
constexpr T& emplace (Args&&... args);
template < class U , class ... Args>
constexpr T& emplace (std::initializer_list<U>, Args&&...);
функция-член обмена и функция друга
// For accessing T's members
// precondition: has_value() == true
constexpr T const * operator ->() const noexcept ;
// precondition: has_value() == true
constexpr T* operator ->() noexcept ;
// Getting reference to T
// precondition: has_value() == true
constexpr T const & operator *() const & noexcept ;
// precondition: has_value() == true
constexpr T& operator *() & noexcept ;
// precondition: has_value() == true
constexpr T&& operator *() && noexcept ;
// precondition: has_value() == true
constexpr T const && operator *() const && noexcept ;
// Query if value exists
constexpr explicit operator bool () const noexcept ;
constexpr bool has_value () const noexcept ;
// Get value, if not exists throws exception rd::bad_expected_access(error())
constexpr T const & value () const &;
constexpr T& value () &;
constexpr T&& value() &&;
constexpr T const && value() const &&;
// Get error, (undefined behavior if error not exists)
constexpr E const & error () const &;
constexpr E& error () &;
constexpr E&& error() &&;
constexpr E const && error() const &&;
// Get the value, if value doesn't exist return v
template < class U >
constexpr T value_or (U&& v) const &;
template < class U >
constexpr T value_or (U&& v) &&;
а потом:
F должен вызываться с ожидаемым типом значения и должен возвращать ожидаемое значение, тип ошибки которого должен быть E. Он возвращает текущую ожидаемую ошибку, если ошибка есть, другое возвращает результат вызова f со значением.
template < class F >
constexpr auto and_then (F&& f) &;
template < class F >
constexpr auto and_then (F&& f) &&;
template < class F >
constexpr auto and_then (F&& f) const &;
template < class F >
constexpr auto and_then (F&& f) const &&;
или_еще:
F должен вызываться с ожидаемым типом ошибки и должен возвращать ожидаемое значение, тип значения которого должен быть T.
Если invoke_result_t<F, E>
— это любая специализация ожидаемого (тип значения которого должен быть таким же, как T), то, если значение есть, значение возвращается, завернутое в ignore_result_t из F, в противном случае возвращается результат вызова f.
Если invoke_result_t<F, E>
недействителен, то при наличии ошибки F вызывается с ошибкой. В качестве результата возвращается текущее ожидаемое значение.
template < class F >
constexpr auto or_else (F&& f) &;
template < class F >
constexpr auto or_else (F&& f) &&;
template < class F >
constexpr auto or_else (F&& f) const &;
template < class F >
constexpr auto or_else (F&& f) const &&;
преобразовать:
F должен вызываться с ожидаемым типом значения тока. Если текущее ожидаемое значение содержит ошибку, результирующее ожидаемое значение содержит эту ошибку, в противном случае оно содержит результат вызова текущего ожидаемого значения с помощью F.
template < class F >
constexpr auto transform (F&& f) &;
template < class F >
constexpr auto transform (F&& f) &&;
template < class F >
constexpr auto transform (F&& f) const &;
template < class F >
constexpr auto transform (F&& f) const &&;
трансформация_ошибка:
F должен быть вызван с ожидаемым типом ошибки тока. Если текущее ожидаемое значение содержит значение, результирующее ожидаемое значение содержит это значение, в противном случае оно содержит результат вызова текущей ожидаемой ошибки с помощью F.
template < class F >
constexpr auto transform_error (F&& f) &;
template < class F >
constexpr auto transform_error (F&& f) &&;
template < class F >
constexpr auto transform_error (F&& f) const &;
template < class F >
constexpr auto transform_error (F&& f) const &&;
template < class U , class G >
constexpr friend bool operator ==(expected const &, expected<U, G> const &);
template < class U >
constexpr friend bool operator ==(expected const &, U const &);
template < class G >
constexpr friend bool operator ==(expected const &, rd::unexpected<G> const &);
template < typename T>
class unexpected
using value_type = T
Копировать конструктор:
constexpr unexpected (unexpected const &);
Переместить конструктор:
constexpr unexpected (unexpected&&);
конструктор in_place_t:
Он принимает тег std::in_place и конструирует T с аргументами.
template < typename ...Args>
constexpr unexpected (std:: in_place_t , Args&&... args);
template < typename U, typename ...Args>
constexpr unexpected (std:: in_place_t , std::initializer_list<U>, Args&&... args);
конструктор значений:
Конструирует T с ошибкой.
template < typename Err>
constexpr unexpected (Err&& err);
// value(): gets the stored T value
constexpr T & value () &;
constexpr T const & value () const &;
constexpr T && value() &&;
constexpr T const && value() const &&;
template < class E2 >
friend constexpr bool operator ==(unexpected const & x, unexpected< E2 > const & y);
bad_expected_access возникает из исключения. Сохраняет значение ошибки.
template < class E >
class bad_expected_access ;
E& error () & noexcept ;
E const & error () const & noexcept ;
E&& error() && noexcept ;
E const && error() const && noexcept ;
Это всего лишь тип тега, обозначающий ожидаемую ошибку построения.
Библиотека также предоставляет неожиданную переменную типа rd::unexpect_t.
inline constexpr unexpect_t unexpect{};