blob: ddab9e5b57de58eb5626959aaceb6daa6043def7 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <algorithm>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include <variant>
// android::base::expected is a partial implementation of C++23's std::expected
// for Android.
//
// Usage:
// using android::base::expected;
// using android::base::unexpected;
//
// expected<double,std::string> safe_divide(double i, double j) {
// if (j == 0) return unexpected("divide by zero");
// else return i / j;
// }
//
// void test() {
// auto q = safe_divide(10, 0);
// if (q.ok()) { printf("%f\n", q.value()); }
// else { printf("%s\n", q.error().c_str()); }
// }
//
// Once the Android platform has moved to C++23, this will be removed and
// android::base::expected will be type aliased to std::expected.
//
namespace android {
namespace base {
// Synopsis
template <class T, class E>
class expected;
template <class E>
class unexpected;
template <class E>
unexpected(E) -> unexpected<E>;
template <class E>
class bad_expected_access;
template <>
class bad_expected_access<void>;
struct unexpect_t {
explicit unexpect_t() = default;
};
inline constexpr unexpect_t unexpect{};
// macros for SFINAE
#define _ENABLE_IF(...) \
, std::enable_if_t<(__VA_ARGS__)>* = nullptr
// Define NODISCARD_EXPECTED to prevent expected<T,E> from being
// ignored when used as a return value. This is off by default.
#ifdef NODISCARD_EXPECTED
#define _NODISCARD_ [[nodiscard]]
#else
#define _NODISCARD_
#endif
#define _EXPLICIT(cond) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wc++20-extensions\"") explicit(cond) \
_Pragma("clang diagnostic pop")
#define _COMMA ,
namespace expected_internal {
template <class T>
struct remove_cvref {
using type = std::remove_cv_t<std::remove_reference_t<T>>;
};
template <class T>
using remove_cvref_t = typename remove_cvref<T>::type;
// Can T be constructed from W (or W converted to T)? W can be lvalue or rvalue,
// const or not.
template <class T, class W>
inline constexpr bool converts_from_any_cvref =
std::disjunction_v<std::is_constructible<T, W&>, std::is_convertible<W&, T>,
std::is_constructible<T, W>, std::is_convertible<W, T>,
std::is_constructible<T, const W&>, std::is_convertible<const W&, T>,
std::is_constructible<T, const W>, std::is_convertible<const W, T>>;
template <class T>
struct is_expected : std::false_type {};
template <class T, class E>
struct is_expected<expected<T, E>> : std::true_type {};
template <class T>
inline constexpr bool is_expected_v = is_expected<T>::value;
template <class T>
struct is_unexpected : std::false_type {};
template <class E>
struct is_unexpected<unexpected<E>> : std::true_type {};
template <class T>
inline constexpr bool is_unexpected_v = is_unexpected<T>::value;
// Constraints on constructing an expected<T, ...> from an expected<U, G>
// related to T and U. UF is either "const U&" or "U".
template <class T, class U, class G, class UF>
inline constexpr bool convert_value_constraints =
std::is_constructible_v<T, UF> &&
(std::is_same_v<std::remove_cv_t<T>, bool> || !converts_from_any_cvref<T, expected<U, G>>);
// Constraints on constructing an expected<..., E> from an expected<U, G>
// related to E, G, and expected<U, G>. GF is either "const G&" or "G".
template <class E, class U, class G, class GF>
inline constexpr bool convert_error_constraints =
std::is_constructible_v<E, GF> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, expected<U, G>> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>&> &&
!std::is_constructible_v<unexpected<E>, const expected<U, G>>;
// If an exception is thrown in expected::operator=, while changing the expected
// object between a value and an error, the expected object is supposed to
// retain its original value, which is only possible if certain constructors
// are noexcept. This implementation doesn't try to be exception-safe, but
// enforce these constraints anyway because std::expected also will enforce
// them, and we intend to switch to it eventually.
template <class T, class E, class Self, class Value>
inline constexpr bool eh_assign_constraints =
std::is_nothrow_constructible_v<Self, Value> ||
std::is_nothrow_move_constructible_v<T> ||
std::is_nothrow_move_constructible_v<E>;
// Implement expected<..., E>::expected([const] unexpected<G> [&/&&]).
#define _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(GF, ParamType, forward_func) \
template <class G _ENABLE_IF(std::is_constructible_v<E, GF>)> \
constexpr _EXPLICIT((!std::is_convertible_v<GF, E>)) \
expected(ParamType e) noexcept(std::is_nothrow_constructible_v<E, GF>) \
: var_(std::in_place_index<1>, forward_func(e.error())) {}
// Implement expected<..., E>::operator=([const] unexpected<G> [&/&&]).
#define _ASSIGN_UNEXPECTED_TO_EXPECTED(GF, ParamType, forward_func, extra_constraints) \
template <class G _ENABLE_IF(std::is_constructible_v<E, GF> && \
std::is_assignable_v<E&, GF>) && \
extra_constraints> \
constexpr expected& operator=(ParamType e) noexcept(std::is_nothrow_constructible_v<E, GF> && \
std::is_nothrow_assignable_v<E&, GF>) { \
if (has_value()) { \
var_.template emplace<1>(forward_func(e.error())); \
} else { \
error() = forward_func(e.error()); \
} \
return *this; \
}
} // namespace expected_internal
// Class expected
template <class T, class E>
class _NODISCARD_ expected {
static_assert(std::is_object_v<T> && !std::is_array_v<T> &&
!std::is_same_v<std::remove_cv_t<T>, std::in_place_t> &&
!std::is_same_v<std::remove_cv_t<T>, unexpect_t> &&
!expected_internal::is_unexpected_v<std::remove_cv_t<T>>,
"expected value type cannot be a reference, a function, an array, in_place_t, "
"unexpect_t, or unexpected");
public:
using value_type = T;
using error_type = E;
using unexpected_type = unexpected<E>;
template <class U>
using rebind = expected<U, error_type>;
// Delegate simple operations to the underlying std::variant. std::variant
// doesn't set noexcept well, at least for copy ctor/assign, so set it
// explicitly. Technically the copy/move assignment operators should also be
// deleted if neither T nor E satisfies is_nothrow_move_constructible_v, but
// that would require making these operator= methods into template functions.
constexpr expected() = default;
constexpr expected(const expected& rhs) noexcept(
std::is_nothrow_copy_constructible_v<T> && std::is_nothrow_copy_constructible_v<E>) = default;
constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v<T> &&
std::is_nothrow_move_constructible_v<E>) = default;
constexpr expected& operator=(const expected& rhs) noexcept(
std::is_nothrow_copy_constructible_v<T> && std::is_nothrow_copy_assignable_v<T> &&
std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) = default;
constexpr expected& operator=(expected&& rhs) noexcept(
std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_assignable_v<T> &&
std::is_nothrow_move_constructible_v<E> && std::is_nothrow_move_assignable_v<E>) = default;
// Construct this expected<T, E> from a different expected<U, G> type.
#define _CONVERTING_CTOR(UF, GF, ParamType, forward_func) \
template <class U, \
class G _ENABLE_IF(expected_internal::convert_value_constraints<T, U, G, UF> && \
expected_internal::convert_error_constraints<E, U, G, GF>)> \
constexpr _EXPLICIT((!std::is_convertible_v<UF, T> || !std::is_convertible_v<GF, E>)) \
expected(ParamType rhs) noexcept(std::is_nothrow_constructible_v<T, UF> && \
std::is_nothrow_constructible_v<E, GF>) \
: var_(rhs.has_value() ? variant_type(std::in_place_index<0>, forward_func(rhs.value())) \
: variant_type(std::in_place_index<1>, forward_func(rhs.error()))) {}
// NOLINTNEXTLINE(google-explicit-constructor)
_CONVERTING_CTOR(const U&, const G&, const expected<U _COMMA G>&, )
// NOLINTNEXTLINE(google-explicit-constructor)
_CONVERTING_CTOR(U, G, expected<U _COMMA G>&&, std::move)
#undef _CONVERTING_CTOR
// Construct from (converted) success value, using a forwarding reference.
template <class U = T _ENABLE_IF(
!std::is_same_v<expected_internal::remove_cvref_t<U>, std::in_place_t> &&
!std::is_same_v<expected_internal::remove_cvref_t<U>, expected> &&
!expected_internal::is_unexpected_v<expected_internal::remove_cvref_t<U>> &&
std::is_constructible_v<T, U> &&
(!std::is_same_v<std::remove_cv_t<T>, bool> ||
!expected_internal::is_expected_v<expected_internal::remove_cvref_t<U>>))>
constexpr _EXPLICIT((!std::is_convertible_v<U, T>))
// NOLINTNEXTLINE(google-explicit-constructor)
expected(U&& v) noexcept(std::is_nothrow_constructible_v<T, U>)
: var_(std::in_place_index<0>, std::forward<U>(v)) {}
// NOLINTNEXTLINE(google-explicit-constructor)
_CONSTRUCT_EXPECTED_FROM_UNEXPECTED(const G&, const unexpected<G>&, )
// NOLINTNEXTLINE(google-explicit-constructor)
_CONSTRUCT_EXPECTED_FROM_UNEXPECTED(G, unexpected<G>&&, std::move)
// in_place_t construction
template <class... Args _ENABLE_IF(std::is_constructible_v<T, Args...>)>
constexpr explicit expected(std::in_place_t, Args&&... args)
noexcept(std::is_nothrow_constructible_v<T, Args...>)
: var_(std::in_place_index<0>, std::forward<Args>(args)...) {}
// in_place_t with initializer_list construction
template <class U, class... Args _ENABLE_IF(
std::is_constructible_v<T, std::initializer_list<U>&, Args...>)>
constexpr explicit expected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
noexcept(std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>)
: var_(std::in_place_index<0>, il, std::forward<Args>(args)...) {}
// unexpect_t construction
template <class... Args _ENABLE_IF(std::is_constructible_v<E, Args...>)>
constexpr explicit expected(unexpect_t, Args&&... args)
noexcept(std::is_nothrow_constructible_v<E, Args...>)
: var_(std::in_place_index<1>, unexpected_type(std::forward<Args>(args)...)) {}
// unexpect_t with initializer_list construction
template <class U, class... Args _ENABLE_IF(
std::is_constructible_v<E, std::initializer_list<U>&, Args...>)>
constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
noexcept(std::is_nothrow_constructible_v<E, std::initializer_list<U>&, Args...>)
: var_(std::in_place_index<1>, unexpected_type(il, std::forward<Args>(args)...)) {}
// Assignment from (converted) success value, using a forwarding reference.
template <class U = T _ENABLE_IF(
!std::is_same_v<expected, expected_internal::remove_cvref_t<U>> &&
!expected_internal::is_unexpected_v<expected_internal::remove_cvref_t<U>> &&
std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
expected_internal::eh_assign_constraints<T, E, T, U>)>
constexpr expected& operator=(U&& v) noexcept(std::is_nothrow_constructible_v<T, U> &&
std::is_nothrow_assignable_v<T&, U>) {
if (has_value()) {
value() = std::forward<U>(v);
} else {
var_.template emplace<0>(std::forward<U>(v));
}
return *this;
}
_ASSIGN_UNEXPECTED_TO_EXPECTED(const G&, const unexpected<G>&, ,
(expected_internal::eh_assign_constraints<T, E, E, const G&>))
_ASSIGN_UNEXPECTED_TO_EXPECTED(G, unexpected<G>&&, std::move,
(expected_internal::eh_assign_constraints<T, E, E, G>))
// modifiers
template <class... Args _ENABLE_IF(std::is_nothrow_constructible_v<T, Args...>)>
constexpr T& emplace(Args&&... args) noexcept {
var_.template emplace<0>(std::forward<Args>(args)...);
return value();
}
template <class U, class... Args _ENABLE_IF(
std::is_nothrow_constructible_v<T, std::initializer_list<U>&, Args...>)>
constexpr T& emplace(std::initializer_list<U> il, Args&&... args) noexcept {
var_.template emplace<0>(il, std::forward<Args>(args)...);
return value();
}
// Swap. This function takes a template argument so that _ENABLE_IF works.
template <class U = T _ENABLE_IF(
std::is_same_v<U, T> &&
std::is_swappable_v<T> && std::is_swappable_v<E> &&
std::is_move_constructible_v<T> && std::is_move_constructible_v<E> &&
(std::is_nothrow_move_constructible_v<T> ||
std::is_nothrow_move_constructible_v<E>))>
constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<T> &&
std::is_nothrow_swappable_v<T> &&
std::is_nothrow_move_constructible_v<E> &&
std::is_nothrow_swappable_v<E>) {
var_.swap(rhs.var_);
}
// observers
constexpr const T* operator->() const { return std::addressof(value()); }
constexpr T* operator->() { return std::addressof(value()); }
constexpr const T& operator*() const& { return value(); }
constexpr T& operator*() & { return value(); }
constexpr const T&& operator*() const&& { return std::move(std::get<T>(var_)); }
constexpr T&& operator*() && { return std::move(std::get<T>(var_)); }
constexpr bool has_value() const noexcept { return var_.index() == 0; }
constexpr bool ok() const noexcept { return has_value(); }
constexpr explicit operator bool() const noexcept { return has_value(); }
constexpr const T& value() const& { return std::get<T>(var_); }
constexpr T& value() & { return std::get<T>(var_); }
constexpr const T&& value() const&& { return std::move(std::get<T>(var_)); }
constexpr T&& value() && { return std::move(std::get<T>(var_)); }
constexpr const E& error() const& { return std::get<unexpected_type>(var_).error(); }
constexpr E& error() & { return std::get<unexpected_type>(var_).error(); }
constexpr const E&& error() const&& { return std::move(std::get<unexpected_type>(var_)).error(); }
constexpr E&& error() && { return std::move(std::get<unexpected_type>(var_)).error(); }
template<class U _ENABLE_IF(
std::is_copy_constructible_v<T> &&
std::is_convertible_v<U, T>
)>
constexpr T value_or(U&& v) const& {
if (has_value()) return value();
else return static_cast<T>(std::forward<U>(v));
}
template<class U _ENABLE_IF(
std::is_move_constructible_v<T> &&
std::is_convertible_v<U, T>
)>
constexpr T value_or(U&& v) && {
if (has_value()) return std::move(value());
else return static_cast<T>(std::forward<U>(v));
}
// expected equality operators
template<class T1, class E1, class T2, class E2>
friend constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y);
template<class T1, class E1, class T2, class E2>
friend constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y);
// Comparison with unexpected<E>
template<class T1, class E1, class E2>
friend constexpr bool operator==(const expected<T1, E1>&, const unexpected<E2>&);
template<class T1, class E1, class E2>
friend constexpr bool operator==(const unexpected<E2>&, const expected<T1, E1>&);
template<class T1, class E1, class E2>
friend constexpr bool operator!=(const expected<T1, E1>&, const unexpected<E2>&);
template<class T1, class E1, class E2>
friend constexpr bool operator!=(const unexpected<E2>&, const expected<T1, E1>&);
private:
using variant_type = std::variant<value_type, unexpected_type>;
variant_type var_;
};
template<class T1, class E1, class T2, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const expected<T2, E2>& y) {
if (x.has_value() != y.has_value()) return false;
if (!x.has_value()) return x.error() == y.error();
return *x == *y;
}
template<class T1, class E1, class T2, class E2>
constexpr bool operator!=(const expected<T1, E1>& x, const expected<T2, E2>& y) {
return !(x == y);
}
// Comparison with unexpected<E>
template<class T1, class E1, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const unexpected<E2>& y) {
return !x.has_value() && (x.error() == y.error());
}
template<class T1, class E1, class E2>
constexpr bool operator==(const unexpected<E2>& x, const expected<T1, E1>& y) {
return !y.has_value() && (x.error() == y.error());
}
template<class T1, class E1, class E2>
constexpr bool operator!=(const expected<T1, E1>& x, const unexpected<E2>& y) {
return x.has_value() || (x.error() != y.error());
}
template<class T1, class E1, class E2>
constexpr bool operator!=(const unexpected<E2>& x, const expected<T1, E1>& y) {
return y.has_value() || (x.error() != y.error());
}
template<class E>
class _NODISCARD_ expected<void, E> {
public:
using value_type = void;
using error_type = E;
using unexpected_type = unexpected<E>;
template <class U>
using rebind = expected<U, error_type>;
// Delegate simple operations to the underlying std::variant.
constexpr expected() = default;
constexpr expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v<E>) =
default;
constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
constexpr expected& operator=(const expected& rhs) noexcept(
std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) = default;
constexpr expected& operator=(expected&& rhs) noexcept(
std::is_nothrow_move_constructible_v<E> && std::is_nothrow_move_assignable_v<E>) = default;
// Construct this expected<void, E> from a different expected<U, G> type.
#define _CONVERTING_CTOR(GF, ParamType, forward_func) \
template <class U, class G _ENABLE_IF(std::is_void_v<U> && \
expected_internal::convert_error_constraints<E, U, G, GF>)> \
constexpr _EXPLICIT((!std::is_convertible_v<GF, E>)) \
expected(ParamType rhs) noexcept(std::is_nothrow_constructible_v<E, GF>) \
: var_(rhs.has_value() ? variant_type(std::in_place_index<0>, std::monostate()) \
: variant_type(std::in_place_index<1>, forward_func(rhs.error()))) {}
// NOLINTNEXTLINE(google-explicit-constructor)
_CONVERTING_CTOR(const G&, const expected<U _COMMA G>&, )
// NOLINTNEXTLINE(google-explicit-constructor)
_CONVERTING_CTOR(G, expected<U _COMMA G>&&, std::move)
#undef _CONVERTING_CTOR
// NOLINTNEXTLINE(google-explicit-constructor)
_CONSTRUCT_EXPECTED_FROM_UNEXPECTED(const G&, const unexpected<G>&, )
// NOLINTNEXTLINE(google-explicit-constructor)
_CONSTRUCT_EXPECTED_FROM_UNEXPECTED(G, unexpected<G>&&, std::move)
// in_place_t construction
constexpr explicit expected(std::in_place_t) noexcept {}
// unexpect_t construction
template <class... Args _ENABLE_IF(std::is_constructible_v<E, Args...>)>
constexpr explicit expected(unexpect_t, Args&&... args)
noexcept(std::is_nothrow_constructible_v<E, Args...>)
: var_(std::in_place_index<1>, unexpected_type(std::forward<Args>(args)...)) {}
// unexpect_t with initializer_list construction
template <class U, class... Args _ENABLE_IF(
std::is_constructible_v<E, std::initializer_list<U>&, Args...>)>
constexpr explicit expected(unexpect_t, std::initializer_list<U> il, Args&&... args)
noexcept(std::is_nothrow_constructible_v<E, std::initializer_list<U>&, Args...>)
: var_(std::in_place_index<1>, unexpected_type(il, std::forward<Args>(args)...)) {}
_ASSIGN_UNEXPECTED_TO_EXPECTED(const G&, const unexpected<G>&, , true)
_ASSIGN_UNEXPECTED_TO_EXPECTED(G, unexpected<G>&&, std::move, true)
// modifiers
constexpr void emplace() noexcept { var_.template emplace<0>(std::monostate()); }
// Swap. This function takes a template argument so that _ENABLE_IF works.
template <class G = E _ENABLE_IF(std::is_same_v<G, E> &&
std::is_swappable_v<E> && std::is_move_constructible_v<E>)>
constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E> &&
std::is_nothrow_swappable_v<E>) {
var_.swap(rhs.var_);
}
// observers
constexpr bool has_value() const noexcept { return var_.index() == 0; }
constexpr bool ok() const noexcept { return has_value(); }
constexpr explicit operator bool() const noexcept { return has_value(); }
constexpr void value() const& { if (!has_value()) std::get<0>(var_); }
constexpr const E& error() const& { return std::get<1>(var_).error(); }
constexpr E& error() & { return std::get<1>(var_).error(); }
constexpr const E&& error() const&& { return std::move(std::get<1>(var_)).error(); }
constexpr E&& error() && { return std::move(std::get<1>(var_)).error(); }
// expected equality operators
template<class E1, class E2>
friend constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y);
private:
using variant_type = std::variant<std::monostate, unexpected_type>;
variant_type var_;
};
template<class E1, class E2>
constexpr bool operator==(const expected<void, E1>& x, const expected<void, E2>& y) {
if (x.has_value() != y.has_value()) return false;
if (!x.has_value()) return x.error() == y.error();
return true;
}
template<class T1, class E1, class E2>
constexpr bool operator==(const expected<T1, E1>& x, const expected<void, E2>& y) {
if (x.has_value() != y.has_value()) return false;
if (!x.has_value()) return x.error() == y.error();
return false;
}
template<class E1, class T2, class E2>
constexpr bool operator==(const expected<void, E1>& x, const expected<T2, E2>& y) {
if (x.has_value() != y.has_value()) return false;
if (!x.has_value()) return x.error() == y.error();
return false;
}
template <class T, class E, typename = decltype(
std::declval<expected<T, E>&>().swap(std::declval<expected<T, E>&>()))>
constexpr void swap(expected<T, E>& x, expected<T, E>& y) noexcept(noexcept(x.swap(y))) {
x.swap(y);
}
template<class E>
class unexpected {
static_assert(std::is_object_v<E> && !std::is_array_v<E> && !std::is_const_v<E> &&
!std::is_volatile_v<E> && !expected_internal::is_unexpected_v<E>,
"unexpected error type cannot be a reference, a function, an array, cv-qualified, "
"or unexpected");
public:
// constructors
constexpr unexpected(const unexpected&) = default;
constexpr unexpected(unexpected&&) = default;
template <class Err = E _ENABLE_IF(
!std::is_same_v<expected_internal::remove_cvref_t<Err>, unexpected> &&
!std::is_same_v<expected_internal::remove_cvref_t<Err>, std::in_place_t> &&
std::is_constructible_v<E, Err>)>
constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
: val_(std::forward<Err>(e)) {}
template <class... Args _ENABLE_IF(std::is_constructible_v<E, Args...>)>
constexpr explicit unexpected(std::in_place_t, Args&&... args)
noexcept(std::is_nothrow_constructible_v<E, Args...>)
: val_(std::forward<Args>(args)...) {}
template <class U, class... Args _ENABLE_IF(
std::is_constructible_v<E, std::initializer_list<U>&, Args...>)>
constexpr explicit unexpected(std::in_place_t, std::initializer_list<U> il, Args&&... args)
noexcept(std::is_nothrow_constructible_v<E, std::initializer_list<U>&, Args...>)
: val_(il, std::forward<Args>(args)...) {}
constexpr unexpected& operator=(const unexpected&) = default;
constexpr unexpected& operator=(unexpected&&) = default;
// observer
constexpr const E& error() const& noexcept { return val_; }
constexpr E& error() & noexcept { return val_; }
constexpr const E&& error() const&& noexcept { return std::move(val_); }
constexpr E&& error() && noexcept { return std::move(val_); }
// Swap. This function takes a template argument so that _ENABLE_IF works.
template <typename G = E _ENABLE_IF(std::is_same_v<G, E> && std::is_swappable_v<E>)>
void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
// Make std::swap visible to provide swap for STL and builtin types, but use
// an unqualified swap to invoke argument-dependent lookup to find the swap
// functions for user-declared types.
using std::swap;
swap(val_, other.val_);
}
template<class E1, class E2>
friend constexpr bool
operator==(const unexpected<E1>& e1, const unexpected<E2>& e2);
template<class E1, class E2>
friend constexpr bool
operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2);
private:
E val_;
};
template<class E1, class E2>
constexpr bool
operator==(const unexpected<E1>& e1, const unexpected<E2>& e2) {
return e1.error() == e2.error();
}
template<class E1, class E2>
constexpr bool
operator!=(const unexpected<E1>& e1, const unexpected<E2>& e2) {
return e1.error() != e2.error();
}
template <class E1 _ENABLE_IF(std::is_swappable_v<E1>)>
void swap(unexpected<E1>& x, unexpected<E1>& y) noexcept(noexcept(x.swap(y))) {
x.swap(y);
}
// TODO: bad_expected_access class
#undef _ENABLE_IF
#undef _NODISCARD_
#undef _EXPLICIT
#undef _COMMA
#undef _CONSTRUCT_EXPECTED_FROM_UNEXPECTED
#undef _ASSIGN_UNEXPECTED_TO_EXPECTED
} // namespace base
} // namespace android