Einzelne Header-std::expected-Implementierung mit C++20.
Die Bibliothek setzt diese beiden Vorschläge um:
Diese Implementierung stellt erwartet im Namespace rd bereit. Verwenden Sie also für jede Verwendung von std::expected rd::expected.
std::expected ist ein Mechanismus zur Fehlerbehandlung. Dies ist dem std::result von Rust sehr ähnlich. std::expected enthält grundsätzlich entweder Ergebnis oder Fehler.
Es gibt wahrscheinlich zwei Gründe dafür, Ausnahmen nicht „immer“ zu verwenden:
Beim Schreiben einer Bibliothek stellt dies ein großes Problem für die Fehlerbehandlung dar. Wenn wir uns in der Bibliothek dafür entscheiden, bei einer Fehlerbedingung eine Ausnahme auszulösen, treffen wir die Wahl für den Bibliotheksbenutzer. Eine Fehlerbedingung in der Bibliothek kann eine Ausnahmesituation im Kontext der Anwendung sein, die diese Bibliothek verwendet, oder auch nicht.
std::expected bietet eine Standardalternative für Ausnahmen, die für Fehlerbedingungen verwendet werden können, die im Kontext der Anwendung keine Ausnahmen darstellen.
Dabei ist zu beachten, dass Ausnahmen bei Panikzuständen gut geeignet sind. Und es gibt viele offensichtliche Panikzustände, für die nur Ausnahmen gelten sollten.
error_code war früher der einfachste Fehlerbehandlungsmechanismus. Historisch gesehen ist error_code eine einfache Ganzzahl, die für einen umfangreichen Fehlerkontext nicht geeignet ist.
Heutzutage stellt der C++-Header <system_error> umfangreicheren Fehlercode bereit. Allerdings führt error_code zur Monopolisierung des Rückkanals. Daher muss der Wert über einen anderen Kanal ausgegeben werden.
Außerdem müssen Sie nach jedem Funktionsaufruf eine if-Bedingung schreiben.
std::expected garantiert, dass es niemals leer sein wird. Das heißt, es würde zu jedem Zeitpunkt entweder einen Wert oder einen Fehler enthalten. Dadurch ist die Bedienung wirklich einfach.
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 können kein Referenztyp sein, da sie auch in Papierform nicht unterstützt werden.
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template < class U >
using rebind = expected<U, error_type>;
Standardkonstruktor:
constexpr expected ();
Konstruktor kopieren:
constexpr expected (expected const &);
Move-Konstruktor:
constexpr expected (expected &&);
Erwarteter Konstruktor:
U sollte in T konvertierbar sein und G sollte in E konvertierbar sein.
template < class U , class G >
constexpr expected (expected<U, G> const &);
template < class U , class G >
constexpr expected (expected<U, G> &&);
Wertkonstruktor:
Konstruktor für erwartetes <T , E>, der einen Wert zur Initialisierung von T akzeptiert (Wertteil des erwarteten Werts).
template < class U = T>
constexpr expected (U&& v);
Unerwarteter Konstruktor:
Konstruktor für erwartet<T, E>, der die rd::unexpected-Instanz akzeptiert, um E zu initialisieren (Fehlerteil von erwartet).
template < class G >
constexpr expected (unexpected<G> const &);
template < class G >
constexpr expected (unexpected<G> &&);
in_place_t-Konstruktor:
Konstruktor für erwartet<T, E>, der das Tag „in_place“ und Argumente (zur Weiterleitung) akzeptiert, um T zu initialisieren.
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-Konstruktor:
Konstruktor für erwartet<T, E>, der das rd::unexpect-Tag und Argumente (zur Weiterleitung) akzeptiert, um E zu initialisieren.
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);
Kopierauftrag:
constexpr expected& operator =(expected const &);
Umzugsauftrag:
constexpr expected& operator =(expected &&);
Wertzuweisung:
Weist dem Erwarteten einen Wert zu
template < class U = T>
constexpr expected& operator =(U&& rhs);
rd::unerwartete Zuweisung:
Weist den erwarteten Fehler zu
template < class G >
constexpr expected& operator =(unexpected<G> const &);
template < class G >
constexpr expected& operator =(unexpected<G> &&);
platzieren:
Akzeptiert Argumente, um einen neuen Wert für den erwarteten Konstruktor zu erstellen und ihm zuzuweisen.
template < class ... Args>
constexpr T& emplace (Args&&... args);
template < class U , class ... Args>
constexpr T& emplace (std::initializer_list<U>, Args&&...);
Tauschen Sie Mitgliedsfunktion und Freundfunktion aus
// 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) &&;
und_dann:
F sollte mit dem Werttyp „expected“ aufrufbar sein und sollte „expected“ zurückgeben, dessen Fehlertyp „E“ sein sollte.
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 &&;
or_else:
F sollte mit dem Fehlertyp „expected“ aufrufbar sein und „expected“ zurückgeben, dessen Werttyp „T“ sein sollte.
Wenn invoke_result_t<F, E>
eine beliebige Spezialisierung von erwartet ist (deren Werttyp mit T identisch sein sollte), wird der Wert, wenn ein Wert vorhanden ist, in invoke_result_t von F eingeschlossen zurückgegeben, andernfalls wird das Ergebnis des Aufrufs von f zurückgegeben.
Wenn invoke_result_t<F, E>
ungültig ist und ein Fehler vorliegt, wird F mit Fehler aufgerufen. Als Ergebnis wird der aktuell erwartete Wert zurückgegeben.
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 &&;
verwandeln:
F sollte mit dem Werttyp „aktuell erwartet“ aufrufbar sein. Wenn der aktuelle erwartete Wert einen Fehler enthält, enthält der resultierende erwartete Wert diesen Fehler, andernfalls enthält er das Ergebnis des Aufrufs des aktuellen erwarteten Werts mit 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 &&;
transform_error:
F sollte mit dem erwarteten Fehlertyp „Strom“ aufrufbar sein. Wenn der aktuelle erwartete Wert einen Wert enthält, enthält der resultierende erwartete Wert diesen Wert, andernfalls enthält er das Ergebnis des Aufrufs des aktuell erwarteten Fehlers mit 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
Konstruktor kopieren:
constexpr unexpected (unexpected const &);
Move-Konstruktor:
constexpr unexpected (unexpected&&);
in_place_t-Konstruktor:
Es akzeptiert ein std::in_place-Tag und erstellt T mit Argumenten.
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);
Wertkonstruktor:
Konstruiert T mit err.
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 leitet sich von einer Ausnahme ab. Speichert den Fehlerwert.
template < class E >
class bad_expected_access ;
E& error () & noexcept ;
E const & error () const & noexcept ;
E&& error() && noexcept ;
E const && error() const && noexcept ;
Dies ist nur ein Tag-Typ, um Konstruktionsfehler für erwartet anzuzeigen.
Die Bibliothek stellt außerdem eine unerwartete Variable vom Typ rd::unexpect_t bereit.
inline constexpr unexpect_t unexpect{};