
The monadic operations in std::expected always check has_value() so we can avoid the execptional path in value() and the assertions in error() by accessing _M_val and _M_unex directly. This means that the monadic operations no longer require _M_unex to be copyable so that it can be thrown from value(), as modified by LWG 3938. This also fixes two incorrect uses of std::move in transform(F&&)& and transform(F&&) const& which I found while making these changes. Now that move-only error types are supported, it's possible to properly test the constraints that LWG 3877 added to and_then and transform. The lwg3877.cc test now does that. libstdc++-v3/ChangeLog: * include/std/expected (expected::and_then, expected::or_else) (expected::transform_error): Use _M_val and _M_unex instead of calling value() and error(), as per LWG 3938. (expected::transform): Likewise. Remove incorrect std::move calls from lvalue overloads. (expected<void, E>::and_then, expected<void, E>::or_else) (expected<void, E>::transform): Use _M_unex instead of calling error(). * testsuite/20_util/expected/lwg3877.cc: Add checks for and_then and transform, and for std::expected<void, E>. * testsuite/20_util/expected/lwg3938.cc: New test.
1819 lines
48 KiB
C++
1819 lines
48 KiB
C++
// <expected> -*- C++ -*-
|
|
|
|
// Copyright The GNU Toolchain Authors.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file include/expected
|
|
* This is a Standard C++ Library header.
|
|
*/
|
|
|
|
#ifndef _GLIBCXX_EXPECTED
|
|
#define _GLIBCXX_EXPECTED
|
|
|
|
#pragma GCC system_header
|
|
|
|
#if __cplusplus > 202002L && __cpp_concepts >= 202002L
|
|
|
|
#include <initializer_list>
|
|
#include <bits/exception.h> // exception
|
|
#include <bits/invoke.h> // __invoke
|
|
#include <bits/stl_construct.h> // construct_at
|
|
#include <bits/utility.h> // in_place_t
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
/**
|
|
* @defgroup expected_values Expected values
|
|
* @addtogroup utilities
|
|
* @since C++23
|
|
* @{
|
|
*/
|
|
|
|
#define __cpp_lib_expected 202211L
|
|
|
|
/// Discriminated union that holds an expected value or an error value.
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
template<typename _Tp, typename _Er>
|
|
class expected;
|
|
|
|
/// Wrapper type used to pass an error value to a `std::expected`.
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
template<typename _Er>
|
|
class unexpected;
|
|
|
|
/// Exception thrown by std::expected when the value() is not present.
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
template<typename _Er>
|
|
class bad_expected_access;
|
|
|
|
template<>
|
|
class bad_expected_access<void> : public exception
|
|
{
|
|
protected:
|
|
bad_expected_access() noexcept { }
|
|
bad_expected_access(const bad_expected_access&) = default;
|
|
bad_expected_access(bad_expected_access&&) = default;
|
|
bad_expected_access& operator=(const bad_expected_access&) = default;
|
|
bad_expected_access& operator=(bad_expected_access&&) = default;
|
|
~bad_expected_access() = default;
|
|
|
|
public:
|
|
|
|
[[nodiscard]]
|
|
const char*
|
|
what() const noexcept override
|
|
{ return "bad access to std::expected without expected value"; }
|
|
};
|
|
|
|
template<typename _Er>
|
|
class bad_expected_access : public bad_expected_access<void> {
|
|
public:
|
|
explicit
|
|
bad_expected_access(_Er __e) : _M_unex(std::move(__e)) { }
|
|
|
|
// XXX const char* what() const noexcept override;
|
|
|
|
[[nodiscard]]
|
|
_Er&
|
|
error() & noexcept
|
|
{ return _M_unex; }
|
|
|
|
[[nodiscard]]
|
|
const _Er&
|
|
error() const & noexcept
|
|
{ return _M_unex; }
|
|
|
|
[[nodiscard]]
|
|
_Er&&
|
|
error() && noexcept
|
|
{ return std::move(_M_unex); }
|
|
|
|
[[nodiscard]]
|
|
const _Er&&
|
|
error() const && noexcept
|
|
{ return std::move(_M_unex); }
|
|
|
|
private:
|
|
_Er _M_unex;
|
|
};
|
|
|
|
/// Tag type for constructing unexpected values in a std::expected
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
struct unexpect_t
|
|
{
|
|
explicit unexpect_t() = default;
|
|
};
|
|
|
|
/// Tag for constructing unexpected values in a std::expected
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
inline constexpr unexpect_t unexpect{};
|
|
|
|
/// @cond undocumented
|
|
namespace __expected
|
|
{
|
|
template<typename _Tp>
|
|
constexpr bool __is_expected = false;
|
|
template<typename _Tp, typename _Er>
|
|
constexpr bool __is_expected<expected<_Tp, _Er>> = true;
|
|
|
|
template<typename _Tp>
|
|
constexpr bool __is_unexpected = false;
|
|
template<typename _Tp>
|
|
constexpr bool __is_unexpected<unexpected<_Tp>> = true;
|
|
|
|
template<typename _Fn, typename _Tp>
|
|
using __result = remove_cvref_t<invoke_result_t<_Fn&&, _Tp&&>>;
|
|
template<typename _Fn, typename _Tp>
|
|
using __result_xform = remove_cv_t<invoke_result_t<_Fn&&, _Tp&&>>;
|
|
template<typename _Fn>
|
|
using __result0 = remove_cvref_t<invoke_result_t<_Fn&&>>;
|
|
template<typename _Fn>
|
|
using __result0_xform = remove_cv_t<invoke_result_t<_Fn&&>>;
|
|
|
|
template<typename _Er>
|
|
concept __can_be_unexpected
|
|
= is_object_v<_Er> && (!is_array_v<_Er>)
|
|
&& (!__expected::__is_unexpected<_Er>)
|
|
&& (!is_const_v<_Er>) && (!is_volatile_v<_Er>);
|
|
|
|
// Tag types for in-place construction from an invocation result.
|
|
struct __in_place_inv { };
|
|
struct __unexpect_inv { };
|
|
}
|
|
/// @endcond
|
|
|
|
template<typename _Er>
|
|
class unexpected
|
|
{
|
|
static_assert( __expected::__can_be_unexpected<_Er> );
|
|
|
|
public:
|
|
constexpr unexpected(const unexpected&) = default;
|
|
constexpr unexpected(unexpected&&) = default;
|
|
|
|
template<typename _Err = _Er>
|
|
requires (!is_same_v<remove_cvref_t<_Err>, unexpected>)
|
|
&& (!is_same_v<remove_cvref_t<_Err>, in_place_t>)
|
|
&& is_constructible_v<_Er, _Err>
|
|
constexpr explicit
|
|
unexpected(_Err&& __e)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Err>)
|
|
: _M_unex(std::forward<_Err>(__e))
|
|
{ }
|
|
|
|
template<typename... _Args>
|
|
requires is_constructible_v<_Er, _Args...>
|
|
constexpr explicit
|
|
unexpected(in_place_t, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Args...>)
|
|
: _M_unex(std::forward<_Args>(__args)...)
|
|
{ }
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...>
|
|
constexpr explicit
|
|
unexpected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&,
|
|
_Args...>)
|
|
: _M_unex(__il, std::forward<_Args>(__args)...)
|
|
{ }
|
|
|
|
constexpr unexpected& operator=(const unexpected&) = default;
|
|
constexpr unexpected& operator=(unexpected&&) = default;
|
|
|
|
|
|
[[nodiscard]]
|
|
constexpr const _Er&
|
|
error() const & noexcept { return _M_unex; }
|
|
|
|
[[nodiscard]]
|
|
constexpr _Er&
|
|
error() & noexcept { return _M_unex; }
|
|
|
|
[[nodiscard]]
|
|
constexpr const _Er&&
|
|
error() const && noexcept { return std::move(_M_unex); }
|
|
|
|
[[nodiscard]]
|
|
constexpr _Er&&
|
|
error() && noexcept { return std::move(_M_unex); }
|
|
|
|
constexpr void
|
|
swap(unexpected& __other) noexcept(is_nothrow_swappable_v<_Er>)
|
|
requires is_swappable_v<_Er>
|
|
{
|
|
using std::swap;
|
|
swap(_M_unex, __other._M_unex);
|
|
}
|
|
|
|
template<typename _Err>
|
|
[[nodiscard]]
|
|
friend constexpr bool
|
|
operator==(const unexpected& __x, const unexpected<_Err>& __y)
|
|
{ return __x._M_unex == __y.error(); }
|
|
|
|
friend constexpr void
|
|
swap(unexpected& __x, unexpected& __y) noexcept(noexcept(__x.swap(__y)))
|
|
requires is_swappable_v<_Er>
|
|
{ __x.swap(__y); }
|
|
|
|
private:
|
|
_Er _M_unex;
|
|
};
|
|
|
|
template<typename _Er> unexpected(_Er) -> unexpected<_Er>;
|
|
|
|
/// @cond undocumented
|
|
namespace __expected
|
|
{
|
|
template<typename _Tp>
|
|
struct _Guard
|
|
{
|
|
static_assert( is_nothrow_move_constructible_v<_Tp> );
|
|
|
|
constexpr explicit
|
|
_Guard(_Tp& __x)
|
|
: _M_guarded(__builtin_addressof(__x)), _M_tmp(std::move(__x)) // nothrow
|
|
{ std::destroy_at(_M_guarded); }
|
|
|
|
constexpr
|
|
~_Guard()
|
|
{
|
|
if (_M_guarded) [[unlikely]]
|
|
std::construct_at(_M_guarded, std::move(_M_tmp));
|
|
}
|
|
|
|
_Guard(const _Guard&) = delete;
|
|
_Guard& operator=(const _Guard&) = delete;
|
|
|
|
constexpr _Tp&&
|
|
release() noexcept
|
|
{
|
|
_M_guarded = nullptr;
|
|
return std::move(_M_tmp);
|
|
}
|
|
|
|
private:
|
|
_Tp* _M_guarded;
|
|
_Tp _M_tmp;
|
|
};
|
|
|
|
// reinit-expected helper from [expected.object.assign]
|
|
template<typename _Tp, typename _Up, typename _Vp>
|
|
constexpr void
|
|
__reinit(_Tp* __newval, _Up* __oldval, _Vp&& __arg)
|
|
noexcept(is_nothrow_constructible_v<_Tp, _Vp>)
|
|
{
|
|
if constexpr (is_nothrow_constructible_v<_Tp, _Vp>)
|
|
{
|
|
std::destroy_at(__oldval);
|
|
std::construct_at(__newval, std::forward<_Vp>(__arg));
|
|
}
|
|
else if constexpr (is_nothrow_move_constructible_v<_Tp>)
|
|
{
|
|
_Tp __tmp(std::forward<_Vp>(__arg)); // might throw
|
|
std::destroy_at(__oldval);
|
|
std::construct_at(__newval, std::move(__tmp));
|
|
}
|
|
else
|
|
{
|
|
_Guard<_Up> __guard(*__oldval);
|
|
std::construct_at(__newval, std::forward<_Vp>(__arg)); // might throw
|
|
__guard.release();
|
|
}
|
|
}
|
|
}
|
|
/// @endcond
|
|
|
|
template<typename _Tp, typename _Er>
|
|
class expected
|
|
{
|
|
static_assert( ! is_reference_v<_Tp> );
|
|
static_assert( ! is_function_v<_Tp> );
|
|
static_assert( ! is_same_v<remove_cv_t<_Tp>, in_place_t> );
|
|
static_assert( ! is_same_v<remove_cv_t<_Tp>, unexpect_t> );
|
|
static_assert( ! __expected::__is_unexpected<remove_cv_t<_Tp>> );
|
|
static_assert( __expected::__can_be_unexpected<_Er> );
|
|
|
|
template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
|
|
static constexpr bool __cons_from_expected
|
|
= __or_v<is_constructible<_Tp, expected<_Up, _Err>&>,
|
|
is_constructible<_Tp, expected<_Up, _Err>>,
|
|
is_constructible<_Tp, const expected<_Up, _Err>&>,
|
|
is_constructible<_Tp, const expected<_Up, _Err>>,
|
|
is_convertible<expected<_Up, _Err>&, _Tp>,
|
|
is_convertible<expected<_Up, _Err>, _Tp>,
|
|
is_convertible<const expected<_Up, _Err>&, _Tp>,
|
|
is_convertible<const expected<_Up, _Err>, _Tp>,
|
|
is_constructible<_Unex, expected<_Up, _Err>&>,
|
|
is_constructible<_Unex, expected<_Up, _Err>>,
|
|
is_constructible<_Unex, const expected<_Up, _Err>&>,
|
|
is_constructible<_Unex, const expected<_Up, _Err>>
|
|
>;
|
|
|
|
template<typename _Up, typename _Err>
|
|
constexpr static bool __explicit_conv
|
|
= __or_v<__not_<is_convertible<_Up, _Tp>>,
|
|
__not_<is_convertible<_Err, _Er>>
|
|
>;
|
|
|
|
template<typename _Up>
|
|
static constexpr bool __same_val
|
|
= is_same_v<typename _Up::value_type, _Tp>;
|
|
|
|
template<typename _Up>
|
|
static constexpr bool __same_err
|
|
= is_same_v<typename _Up::error_type, _Er>;
|
|
|
|
public:
|
|
using value_type = _Tp;
|
|
using error_type = _Er;
|
|
using unexpected_type = unexpected<_Er>;
|
|
|
|
template<typename _Up>
|
|
using rebind = expected<_Up, error_type>;
|
|
|
|
constexpr
|
|
expected()
|
|
noexcept(is_nothrow_default_constructible_v<_Tp>)
|
|
requires is_default_constructible_v<_Tp>
|
|
: _M_val(), _M_has_value(true)
|
|
{ }
|
|
|
|
expected(const expected&) = default;
|
|
|
|
constexpr
|
|
expected(const expected& __x)
|
|
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
|
|
is_nothrow_copy_constructible<_Er>>)
|
|
requires is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Er>
|
|
&& (!is_trivially_copy_constructible_v<_Tp>
|
|
|| !is_trivially_copy_constructible_v<_Er>)
|
|
: _M_has_value(__x._M_has_value)
|
|
{
|
|
if (_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_val), __x._M_val);
|
|
else
|
|
std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
|
|
}
|
|
|
|
expected(expected&&) = default;
|
|
|
|
constexpr
|
|
expected(expected&& __x)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
|
is_nothrow_move_constructible<_Er>>)
|
|
requires is_move_constructible_v<_Tp> && is_move_constructible_v<_Er>
|
|
&& (!is_trivially_move_constructible_v<_Tp>
|
|
|| !is_trivially_move_constructible_v<_Er>)
|
|
: _M_has_value(__x._M_has_value)
|
|
{
|
|
if (_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_val),
|
|
std::move(__x)._M_val);
|
|
else
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x)._M_unex);
|
|
}
|
|
|
|
template<typename _Up, typename _Gr>
|
|
requires is_constructible_v<_Tp, const _Up&>
|
|
&& is_constructible_v<_Er, const _Gr&>
|
|
&& (!__cons_from_expected<_Up, _Gr>)
|
|
constexpr explicit(__explicit_conv<const _Up&, const _Gr&>)
|
|
expected(const expected<_Up, _Gr>& __x)
|
|
noexcept(__and_v<is_nothrow_constructible<_Tp, const _Up&>,
|
|
is_nothrow_constructible<_Er, const _Gr&>>)
|
|
: _M_has_value(__x._M_has_value)
|
|
{
|
|
if (_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_val), __x._M_val);
|
|
else
|
|
std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
|
|
}
|
|
|
|
template<typename _Up, typename _Gr>
|
|
requires is_constructible_v<_Tp, _Up>
|
|
&& is_constructible_v<_Er, _Gr>
|
|
&& (!__cons_from_expected<_Up, _Gr>)
|
|
constexpr explicit(__explicit_conv<_Up, _Gr>)
|
|
expected(expected<_Up, _Gr>&& __x)
|
|
noexcept(__and_v<is_nothrow_constructible<_Tp, _Up>,
|
|
is_nothrow_constructible<_Er, _Gr>>)
|
|
: _M_has_value(__x._M_has_value)
|
|
{
|
|
if (_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_val),
|
|
std::move(__x)._M_val);
|
|
else
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x)._M_unex);
|
|
}
|
|
|
|
template<typename _Up = _Tp>
|
|
requires (!is_same_v<remove_cvref_t<_Up>, expected>)
|
|
&& (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
|
|
&& (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
|
|
&& is_constructible_v<_Tp, _Up>
|
|
constexpr explicit(!is_convertible_v<_Up, _Tp>)
|
|
expected(_Up&& __v)
|
|
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
|
|
: _M_val(std::forward<_Up>(__v)), _M_has_value(true)
|
|
{ }
|
|
|
|
template<typename _Gr = _Er>
|
|
requires is_constructible_v<_Er, const _Gr&>
|
|
constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
|
|
expected(const unexpected<_Gr>& __u)
|
|
noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
|
|
: _M_unex(__u.error()), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename _Gr = _Er>
|
|
requires is_constructible_v<_Er, _Gr>
|
|
constexpr explicit(!is_convertible_v<_Gr, _Er>)
|
|
expected(unexpected<_Gr>&& __u)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Gr>)
|
|
: _M_unex(std::move(__u).error()), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename... _Args>
|
|
requires is_constructible_v<_Tp, _Args...>
|
|
constexpr explicit
|
|
expected(in_place_t, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
|
|
: _M_val(std::forward<_Args>(__args)...), _M_has_value(true)
|
|
{ }
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>
|
|
constexpr explicit
|
|
expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&,
|
|
_Args...>)
|
|
: _M_val(__il, std::forward<_Args>(__args)...), _M_has_value(true)
|
|
{ }
|
|
|
|
template<typename... _Args>
|
|
requires is_constructible_v<_Er, _Args...>
|
|
constexpr explicit
|
|
expected(unexpect_t, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Args...>)
|
|
: _M_unex(std::forward<_Args>(__args)...), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...>
|
|
constexpr explicit
|
|
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&,
|
|
_Args...>)
|
|
: _M_unex(__il, std::forward<_Args>(__args)...), _M_has_value(false)
|
|
{ }
|
|
|
|
constexpr ~expected() = default;
|
|
|
|
constexpr ~expected()
|
|
requires (!is_trivially_destructible_v<_Tp>)
|
|
|| (!is_trivially_destructible_v<_Er>)
|
|
{
|
|
if (_M_has_value)
|
|
std::destroy_at(__builtin_addressof(_M_val));
|
|
else
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
}
|
|
|
|
// assignment
|
|
|
|
expected& operator=(const expected&) = delete;
|
|
|
|
constexpr expected&
|
|
operator=(const expected& __x)
|
|
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
|
|
is_nothrow_copy_constructible<_Er>,
|
|
is_nothrow_copy_assignable<_Tp>,
|
|
is_nothrow_copy_assignable<_Er>>)
|
|
requires is_copy_assignable_v<_Tp> && is_copy_constructible_v<_Tp>
|
|
&& is_copy_assignable_v<_Er> && is_copy_constructible_v<_Er>
|
|
&& (is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
{
|
|
if (__x._M_has_value)
|
|
this->_M_assign_val(__x._M_val);
|
|
else
|
|
this->_M_assign_unex(__x._M_unex);
|
|
return *this;
|
|
}
|
|
|
|
constexpr expected&
|
|
operator=(expected&& __x)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
|
is_nothrow_move_constructible<_Er>,
|
|
is_nothrow_move_assignable<_Tp>,
|
|
is_nothrow_move_assignable<_Er>>)
|
|
requires is_move_assignable_v<_Tp> && is_move_constructible_v<_Tp>
|
|
&& is_move_assignable_v<_Er> && is_move_constructible_v<_Er>
|
|
&& (is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
{
|
|
if (__x._M_has_value)
|
|
_M_assign_val(std::move(__x._M_val));
|
|
else
|
|
_M_assign_unex(std::move(__x._M_unex));
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Up = _Tp>
|
|
requires (!is_same_v<expected, remove_cvref_t<_Up>>)
|
|
&& (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
|
|
&& is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up>
|
|
&& (is_nothrow_constructible_v<_Tp, _Up>
|
|
|| is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
constexpr expected&
|
|
operator=(_Up&& __v)
|
|
{
|
|
_M_assign_val(std::forward<_Up>(__v));
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Gr>
|
|
requires is_constructible_v<_Er, const _Gr&>
|
|
&& is_assignable_v<_Er&, const _Gr&>
|
|
&& (is_nothrow_constructible_v<_Er, const _Gr&>
|
|
|| is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
constexpr expected&
|
|
operator=(const unexpected<_Gr>& __e)
|
|
{
|
|
_M_assign_unex(__e.error());
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Gr>
|
|
requires is_constructible_v<_Er, _Gr>
|
|
&& is_assignable_v<_Er&, _Gr>
|
|
&& (is_nothrow_constructible_v<_Er, _Gr>
|
|
|| is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
constexpr expected&
|
|
operator=(unexpected<_Gr>&& __e)
|
|
{
|
|
_M_assign_unex(std::move(__e).error());
|
|
return *this;
|
|
}
|
|
|
|
// modifiers
|
|
|
|
template<typename... _Args>
|
|
requires is_nothrow_constructible_v<_Tp, _Args...>
|
|
constexpr _Tp&
|
|
emplace(_Args&&... __args) noexcept
|
|
{
|
|
if (_M_has_value)
|
|
std::destroy_at(__builtin_addressof(_M_val));
|
|
else
|
|
{
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
_M_has_value = true;
|
|
}
|
|
std::construct_at(__builtin_addressof(_M_val),
|
|
std::forward<_Args>(__args)...);
|
|
return _M_val;
|
|
}
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_nothrow_constructible_v<_Tp, initializer_list<_Up>&,
|
|
_Args...>
|
|
constexpr _Tp&
|
|
emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept
|
|
{
|
|
if (_M_has_value)
|
|
std::destroy_at(__builtin_addressof(_M_val));
|
|
else
|
|
{
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
_M_has_value = true;
|
|
}
|
|
std::construct_at(__builtin_addressof(_M_val),
|
|
__il, std::forward<_Args>(__args)...);
|
|
return _M_val;
|
|
}
|
|
|
|
// swap
|
|
constexpr void
|
|
swap(expected& __x)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
|
is_nothrow_move_constructible<_Er>,
|
|
is_nothrow_swappable<_Tp&>,
|
|
is_nothrow_swappable<_Er&>>)
|
|
requires is_swappable_v<_Tp> && is_swappable_v<_Er>
|
|
&& is_move_constructible_v<_Tp>
|
|
&& is_move_constructible_v<_Er>
|
|
&& (is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
{
|
|
if (_M_has_value)
|
|
{
|
|
if (__x._M_has_value)
|
|
{
|
|
using std::swap;
|
|
swap(_M_val, __x._M_val);
|
|
}
|
|
else
|
|
this->_M_swap_val_unex(__x);
|
|
}
|
|
else
|
|
{
|
|
if (__x._M_has_value)
|
|
__x._M_swap_val_unex(*this);
|
|
else
|
|
{
|
|
using std::swap;
|
|
swap(_M_unex, __x._M_unex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// observers
|
|
|
|
[[nodiscard]]
|
|
constexpr const _Tp*
|
|
operator->() const noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return __builtin_addressof(_M_val);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr _Tp*
|
|
operator->() noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return __builtin_addressof(_M_val);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr const _Tp&
|
|
operator*() const & noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return _M_val;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr _Tp&
|
|
operator*() & noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return _M_val;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr const _Tp&&
|
|
operator*() const && noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return std::move(_M_val);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr _Tp&&
|
|
operator*() && noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return std::move(_M_val);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr explicit
|
|
operator bool() const noexcept { return _M_has_value; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool has_value() const noexcept { return _M_has_value; }
|
|
|
|
constexpr const _Tp&
|
|
value() const &
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return _M_val;
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
|
|
}
|
|
|
|
constexpr _Tp&
|
|
value() &
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return _M_val;
|
|
const auto& __unex = _M_unex;
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(__unex));
|
|
}
|
|
|
|
constexpr const _Tp&&
|
|
value() const &&
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return std::move(_M_val);
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
|
|
}
|
|
|
|
constexpr _Tp&&
|
|
value() &&
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return std::move(_M_val);
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
|
|
}
|
|
|
|
constexpr const _Er&
|
|
error() const & noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return _M_unex;
|
|
}
|
|
|
|
constexpr _Er&
|
|
error() & noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return _M_unex;
|
|
}
|
|
|
|
constexpr const _Er&&
|
|
error() const && noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
constexpr _Er&&
|
|
error() && noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
template<typename _Up>
|
|
constexpr _Tp
|
|
value_or(_Up&& __v) const &
|
|
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
|
|
is_nothrow_convertible<_Up, _Tp>>)
|
|
{
|
|
static_assert( is_copy_constructible_v<_Tp> );
|
|
static_assert( is_convertible_v<_Up, _Tp> );
|
|
|
|
if (_M_has_value)
|
|
return _M_val;
|
|
return static_cast<_Tp>(std::forward<_Up>(__v));
|
|
}
|
|
|
|
template<typename _Up>
|
|
constexpr _Tp
|
|
value_or(_Up&& __v) &&
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
|
is_nothrow_convertible<_Up, _Tp>>)
|
|
{
|
|
static_assert( is_move_constructible_v<_Tp> );
|
|
static_assert( is_convertible_v<_Up, _Tp> );
|
|
|
|
if (_M_has_value)
|
|
return std::move(_M_val);
|
|
return static_cast<_Tp>(std::forward<_Up>(__v));
|
|
}
|
|
|
|
template<typename _Gr = _Er>
|
|
constexpr _Er
|
|
error_or(_Gr&& __e) const&
|
|
{
|
|
static_assert( is_copy_constructible_v<_Er> );
|
|
static_assert( is_convertible_v<_Gr, _Er> );
|
|
|
|
if (_M_has_value)
|
|
return std::forward<_Gr>(__e);
|
|
return _M_unex;
|
|
}
|
|
|
|
template<typename _Gr = _Er>
|
|
constexpr _Er
|
|
error_or(_Gr&& __e) &&
|
|
{
|
|
static_assert( is_move_constructible_v<_Er> );
|
|
static_assert( is_convertible_v<_Gr, _Er> );
|
|
|
|
if (_M_has_value)
|
|
return std::forward<_Gr>(__e);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
// monadic operations
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, _Er&>
|
|
constexpr auto
|
|
and_then(_Fn&& __f) &
|
|
{
|
|
using _Up = __expected::__result<_Fn, _Tp&>;
|
|
static_assert(__expected::__is_expected<_Up>);
|
|
static_assert(is_same_v<typename _Up::error_type, _Er>);
|
|
|
|
if (has_value())
|
|
return std::__invoke(std::forward<_Fn>(__f), _M_val);
|
|
else
|
|
return _Up(unexpect, _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
|
|
constexpr auto
|
|
and_then(_Fn&& __f) const &
|
|
{
|
|
using _Up = __expected::__result<_Fn, const _Tp&>;
|
|
static_assert(__expected::__is_expected<_Up>);
|
|
static_assert(is_same_v<typename _Up::error_type, _Er>);
|
|
|
|
if (has_value())
|
|
return std::__invoke(std::forward<_Fn>(__f), _M_val);
|
|
else
|
|
return _Up(unexpect, _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, _Er>
|
|
constexpr auto
|
|
and_then(_Fn&& __f) &&
|
|
{
|
|
using _Up = __expected::__result<_Fn, _Tp&&>;
|
|
static_assert(__expected::__is_expected<_Up>);
|
|
static_assert(is_same_v<typename _Up::error_type, _Er>);
|
|
|
|
if (has_value())
|
|
return std::__invoke(std::forward<_Fn>(__f), std::move(_M_val));
|
|
else
|
|
return _Up(unexpect, std::move(_M_unex));
|
|
}
|
|
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, const _Er>
|
|
constexpr auto
|
|
and_then(_Fn&& __f) const &&
|
|
{
|
|
using _Up = __expected::__result<_Fn, const _Tp&&>;
|
|
static_assert(__expected::__is_expected<_Up>);
|
|
static_assert(is_same_v<typename _Up::error_type, _Er>);
|
|
|
|
if (has_value())
|
|
return std::__invoke(std::forward<_Fn>(__f), std::move(_M_val));
|
|
else
|
|
return _Up(unexpect, std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Tp, _Tp&>
|
|
constexpr auto
|
|
or_else(_Fn&& __f) &
|
|
{
|
|
using _Gr = __expected::__result<_Fn, _Er&>;
|
|
static_assert(__expected::__is_expected<_Gr>);
|
|
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
|
|
|
|
if (has_value())
|
|
return _Gr(in_place, _M_val);
|
|
else
|
|
return std::__invoke(std::forward<_Fn>(__f), _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Tp, const _Tp&>
|
|
constexpr auto
|
|
or_else(_Fn&& __f) const &
|
|
{
|
|
using _Gr = __expected::__result<_Fn, const _Er&>;
|
|
static_assert(__expected::__is_expected<_Gr>);
|
|
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
|
|
|
|
if (has_value())
|
|
return _Gr(in_place, _M_val);
|
|
else
|
|
return std::__invoke(std::forward<_Fn>(__f), _M_unex);
|
|
}
|
|
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Tp, _Tp>
|
|
constexpr auto
|
|
or_else(_Fn&& __f) &&
|
|
{
|
|
using _Gr = __expected::__result<_Fn, _Er&&>;
|
|
static_assert(__expected::__is_expected<_Gr>);
|
|
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
|
|
|
|
if (has_value())
|
|
return _Gr(in_place, std::move(_M_val));
|
|
else
|
|
return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Tp, const _Tp>
|
|
constexpr auto
|
|
or_else(_Fn&& __f) const &&
|
|
{
|
|
using _Gr = __expected::__result<_Fn, const _Er&&>;
|
|
static_assert(__expected::__is_expected<_Gr>);
|
|
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
|
|
|
|
if (has_value())
|
|
return _Gr(in_place, std::move(_M_val));
|
|
else
|
|
return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, _Er&>
|
|
constexpr auto
|
|
transform(_Fn&& __f) &
|
|
{
|
|
using _Up = __expected::__result_xform<_Fn, _Tp&>;
|
|
using _Res = expected<_Up, _Er>;
|
|
|
|
if (has_value())
|
|
return _Res(__in_place_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
_M_val);
|
|
});
|
|
else
|
|
return _Res(unexpect, _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
|
|
constexpr auto
|
|
transform(_Fn&& __f) const &
|
|
{
|
|
using _Up = __expected::__result_xform<_Fn, const _Tp&>;
|
|
using _Res = expected<_Up, _Er>;
|
|
|
|
if (has_value())
|
|
return _Res(__in_place_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
_M_val);
|
|
});
|
|
else
|
|
return _Res(unexpect, _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, _Er>
|
|
constexpr auto
|
|
transform(_Fn&& __f) &&
|
|
{
|
|
using _Up = __expected::__result_xform<_Fn, _Tp>;
|
|
using _Res = expected<_Up, _Er>;
|
|
|
|
if (has_value())
|
|
return _Res(__in_place_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
std::move(_M_val));
|
|
});
|
|
else
|
|
return _Res(unexpect, std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, const _Er>
|
|
constexpr auto
|
|
transform(_Fn&& __f) const &&
|
|
{
|
|
using _Up = __expected::__result_xform<_Fn, const _Tp>;
|
|
using _Res = expected<_Up, _Er>;
|
|
|
|
if (has_value())
|
|
return _Res(__in_place_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
std::move(_M_val));
|
|
});
|
|
else
|
|
return _Res(unexpect, std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Tp, _Tp&>
|
|
constexpr auto
|
|
transform_error(_Fn&& __f) &
|
|
{
|
|
using _Gr = __expected::__result_xform<_Fn, _Er&>;
|
|
using _Res = expected<_Tp, _Gr>;
|
|
|
|
if (has_value())
|
|
return _Res(in_place, _M_val);
|
|
else
|
|
return _Res(__unexpect_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
_M_unex);
|
|
});
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Tp, const _Tp&>
|
|
constexpr auto
|
|
transform_error(_Fn&& __f) const &
|
|
{
|
|
using _Gr = __expected::__result_xform<_Fn, const _Er&>;
|
|
using _Res = expected<_Tp, _Gr>;
|
|
|
|
if (has_value())
|
|
return _Res(in_place, _M_val);
|
|
else
|
|
return _Res(__unexpect_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
_M_unex);
|
|
});
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Tp, _Tp>
|
|
constexpr auto
|
|
transform_error(_Fn&& __f) &&
|
|
{
|
|
using _Gr = __expected::__result_xform<_Fn, _Er&&>;
|
|
using _Res = expected<_Tp, _Gr>;
|
|
|
|
if (has_value())
|
|
return _Res(in_place, std::move(_M_val));
|
|
else
|
|
return _Res(__unexpect_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
std::move(_M_unex));
|
|
});
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Tp, const _Tp>
|
|
constexpr auto
|
|
transform_error(_Fn&& __f) const &&
|
|
{
|
|
using _Gr = __expected::__result_xform<_Fn, const _Er&&>;
|
|
using _Res = expected<_Tp, _Gr>;
|
|
|
|
if (has_value())
|
|
return _Res(in_place, std::move(_M_val));
|
|
else
|
|
return _Res(__unexpect_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
std::move(_M_unex));
|
|
});
|
|
}
|
|
|
|
// equality operators
|
|
|
|
template<typename _Up, typename _Er2>
|
|
requires (!is_void_v<_Up>)
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const expected<_Up, _Er2>& __y)
|
|
// FIXME: noexcept(noexcept(bool(*__x == *__y))
|
|
// && noexcept(bool(__x.error() == __y.error())))
|
|
{
|
|
if (__x.has_value())
|
|
return __y.has_value() && bool(*__x == *__y);
|
|
else
|
|
return !__y.has_value() && bool(__x.error() == __y.error());
|
|
}
|
|
|
|
template<typename _Up>
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const _Up& __v)
|
|
// FIXME: noexcept(noexcept(bool(*__x == __v)))
|
|
{ return __x.has_value() && bool(*__x == __v); }
|
|
|
|
template<typename _Er2>
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const unexpected<_Er2>& __e)
|
|
// FIXME: noexcept(noexcept(bool(__x.error() == __e.error())))
|
|
{ return !__x.has_value() && bool(__x.error() == __e.error()); }
|
|
|
|
friend constexpr void
|
|
swap(expected& __x, expected& __y)
|
|
noexcept(noexcept(__x.swap(__y)))
|
|
requires requires {__x.swap(__y);}
|
|
{ __x.swap(__y); }
|
|
|
|
private:
|
|
template<typename, typename> friend class expected;
|
|
|
|
template<typename _Vp>
|
|
constexpr void
|
|
_M_assign_val(_Vp&& __v)
|
|
{
|
|
if (_M_has_value)
|
|
_M_val = std::forward<_Vp>(__v);
|
|
else
|
|
{
|
|
__expected::__reinit(__builtin_addressof(_M_val),
|
|
__builtin_addressof(_M_unex),
|
|
std::forward<_Vp>(__v));
|
|
_M_has_value = true;
|
|
}
|
|
}
|
|
|
|
template<typename _Vp>
|
|
constexpr void
|
|
_M_assign_unex(_Vp&& __v)
|
|
{
|
|
if (_M_has_value)
|
|
{
|
|
__expected::__reinit(__builtin_addressof(_M_unex),
|
|
__builtin_addressof(_M_val),
|
|
std::forward<_Vp>(__v));
|
|
_M_has_value = false;
|
|
}
|
|
else
|
|
_M_unex = std::forward<_Vp>(__v);
|
|
}
|
|
|
|
// Swap two expected objects when only one has a value.
|
|
// Precondition: this->_M_has_value && !__rhs._M_has_value
|
|
constexpr void
|
|
_M_swap_val_unex(expected& __rhs)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Er>,
|
|
is_nothrow_move_constructible<_Tp>>)
|
|
{
|
|
if constexpr (is_nothrow_move_constructible_v<_Er>)
|
|
{
|
|
__expected::_Guard<_Er> __guard(__rhs._M_unex);
|
|
std::construct_at(__builtin_addressof(__rhs._M_val),
|
|
std::move(_M_val)); // might throw
|
|
__rhs._M_has_value = true;
|
|
std::destroy_at(__builtin_addressof(_M_val));
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
__guard.release());
|
|
_M_has_value = false;
|
|
}
|
|
else
|
|
{
|
|
__expected::_Guard<_Tp> __guard(_M_val);
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__rhs._M_unex)); // might throw
|
|
_M_has_value = false;
|
|
std::destroy_at(__builtin_addressof(__rhs._M_unex));
|
|
std::construct_at(__builtin_addressof(__rhs._M_val),
|
|
__guard.release());
|
|
__rhs._M_has_value = true;
|
|
}
|
|
}
|
|
|
|
using __in_place_inv = __expected::__in_place_inv;
|
|
using __unexpect_inv = __expected::__unexpect_inv;
|
|
|
|
template<typename _Fn>
|
|
explicit constexpr
|
|
expected(__in_place_inv, _Fn&& __fn)
|
|
: _M_val(std::forward<_Fn>(__fn)()), _M_has_value(true)
|
|
{ }
|
|
|
|
template<typename _Fn>
|
|
explicit constexpr
|
|
expected(__unexpect_inv, _Fn&& __fn)
|
|
: _M_unex(std::forward<_Fn>(__fn)()), _M_has_value(false)
|
|
{ }
|
|
|
|
union {
|
|
_Tp _M_val;
|
|
_Er _M_unex;
|
|
};
|
|
|
|
bool _M_has_value;
|
|
};
|
|
|
|
// Partial specialization for std::expected<cv void, E>
|
|
template<typename _Tp, typename _Er> requires is_void_v<_Tp>
|
|
class expected<_Tp, _Er>
|
|
{
|
|
static_assert( __expected::__can_be_unexpected<_Er> );
|
|
|
|
template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
|
|
static constexpr bool __cons_from_expected
|
|
= __or_v<is_constructible<_Unex, expected<_Up, _Err>&>,
|
|
is_constructible<_Unex, expected<_Up, _Err>>,
|
|
is_constructible<_Unex, const expected<_Up, _Err>&>,
|
|
is_constructible<_Unex, const expected<_Up, _Err>>
|
|
>;
|
|
|
|
template<typename _Up>
|
|
static constexpr bool __same_val
|
|
= is_same_v<typename _Up::value_type, _Tp>;
|
|
|
|
template<typename _Up>
|
|
static constexpr bool __same_err
|
|
= is_same_v<typename _Up::error_type, _Er>;
|
|
|
|
public:
|
|
using value_type = _Tp;
|
|
using error_type = _Er;
|
|
using unexpected_type = unexpected<_Er>;
|
|
|
|
template<typename _Up>
|
|
using rebind = expected<_Up, error_type>;
|
|
|
|
constexpr
|
|
expected() noexcept
|
|
: _M_void(), _M_has_value(true)
|
|
{ }
|
|
|
|
expected(const expected&) = default;
|
|
|
|
constexpr
|
|
expected(const expected& __x)
|
|
noexcept(is_nothrow_copy_constructible_v<_Er>)
|
|
requires is_copy_constructible_v<_Er>
|
|
&& (!is_trivially_copy_constructible_v<_Er>)
|
|
: _M_void(), _M_has_value(__x._M_has_value)
|
|
{
|
|
if (!_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
|
|
}
|
|
|
|
expected(expected&&) = default;
|
|
|
|
constexpr
|
|
expected(expected&& __x)
|
|
noexcept(is_nothrow_move_constructible_v<_Er>)
|
|
requires is_move_constructible_v<_Er>
|
|
&& (!is_trivially_move_constructible_v<_Er>)
|
|
: _M_void(), _M_has_value(__x._M_has_value)
|
|
{
|
|
if (!_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x)._M_unex);
|
|
}
|
|
|
|
template<typename _Up, typename _Gr>
|
|
requires is_void_v<_Up>
|
|
&& is_constructible_v<_Er, const _Gr&>
|
|
&& (!__cons_from_expected<_Up, _Gr>)
|
|
constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
|
|
expected(const expected<_Up, _Gr>& __x)
|
|
noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
|
|
: _M_void(), _M_has_value(__x._M_has_value)
|
|
{
|
|
if (!_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
|
|
}
|
|
|
|
template<typename _Up, typename _Gr>
|
|
requires is_void_v<_Up>
|
|
&& is_constructible_v<_Er, _Gr>
|
|
&& (!__cons_from_expected<_Up, _Gr>)
|
|
constexpr explicit(!is_convertible_v<_Gr, _Er>)
|
|
expected(expected<_Up, _Gr>&& __x)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Gr>)
|
|
: _M_void(), _M_has_value(__x._M_has_value)
|
|
{
|
|
if (!_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x)._M_unex);
|
|
}
|
|
|
|
template<typename _Gr = _Er>
|
|
requires is_constructible_v<_Er, const _Gr&>
|
|
constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
|
|
expected(const unexpected<_Gr>& __u)
|
|
noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
|
|
: _M_unex(__u.error()), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename _Gr = _Er>
|
|
requires is_constructible_v<_Er, _Gr>
|
|
constexpr explicit(!is_convertible_v<_Gr, _Er>)
|
|
expected(unexpected<_Gr>&& __u)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Gr>)
|
|
: _M_unex(std::move(__u).error()), _M_has_value(false)
|
|
{ }
|
|
|
|
constexpr explicit
|
|
expected(in_place_t) noexcept
|
|
: expected()
|
|
{ }
|
|
|
|
template<typename... _Args>
|
|
requires is_constructible_v<_Er, _Args...>
|
|
constexpr explicit
|
|
expected(unexpect_t, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Args...>)
|
|
: _M_unex(std::forward<_Args>(__args)...), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...>
|
|
constexpr explicit
|
|
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&,
|
|
_Args...>)
|
|
: _M_unex(__il, std::forward<_Args>(__args)...), _M_has_value(false)
|
|
{ }
|
|
|
|
constexpr ~expected() = default;
|
|
|
|
constexpr ~expected() requires (!is_trivially_destructible_v<_Er>)
|
|
{
|
|
if (!_M_has_value)
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
}
|
|
|
|
// assignment
|
|
|
|
expected& operator=(const expected&) = delete;
|
|
|
|
constexpr expected&
|
|
operator=(const expected& __x)
|
|
noexcept(__and_v<is_nothrow_copy_constructible<_Er>,
|
|
is_nothrow_copy_assignable<_Er>>)
|
|
requires is_copy_constructible_v<_Er>
|
|
&& is_copy_assignable_v<_Er>
|
|
{
|
|
if (__x._M_has_value)
|
|
emplace();
|
|
else
|
|
_M_assign_unex(__x._M_unex);
|
|
return *this;
|
|
}
|
|
|
|
constexpr expected&
|
|
operator=(expected&& __x)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Er>,
|
|
is_nothrow_move_assignable<_Er>>)
|
|
requires is_move_constructible_v<_Er>
|
|
&& is_move_assignable_v<_Er>
|
|
{
|
|
if (__x._M_has_value)
|
|
emplace();
|
|
else
|
|
_M_assign_unex(std::move(__x._M_unex));
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Gr>
|
|
requires is_constructible_v<_Er, const _Gr&>
|
|
&& is_assignable_v<_Er&, const _Gr&>
|
|
constexpr expected&
|
|
operator=(const unexpected<_Gr>& __e)
|
|
{
|
|
_M_assign_unex(__e.error());
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Gr>
|
|
requires is_constructible_v<_Er, _Gr>
|
|
&& is_assignable_v<_Er&, _Gr>
|
|
constexpr expected&
|
|
operator=(unexpected<_Gr>&& __e)
|
|
{
|
|
_M_assign_unex(std::move(__e.error()));
|
|
return *this;
|
|
}
|
|
|
|
// modifiers
|
|
|
|
constexpr void
|
|
emplace() noexcept
|
|
{
|
|
if (!_M_has_value)
|
|
{
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
_M_has_value = true;
|
|
}
|
|
}
|
|
|
|
// swap
|
|
constexpr void
|
|
swap(expected& __x)
|
|
noexcept(__and_v<is_nothrow_swappable<_Er&>,
|
|
is_nothrow_move_constructible<_Er>>)
|
|
requires is_swappable_v<_Er> && is_move_constructible_v<_Er>
|
|
{
|
|
if (_M_has_value)
|
|
{
|
|
if (!__x._M_has_value)
|
|
{
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x._M_unex)); // might throw
|
|
std::destroy_at(__builtin_addressof(__x._M_unex));
|
|
_M_has_value = false;
|
|
__x._M_has_value = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (__x._M_has_value)
|
|
{
|
|
std::construct_at(__builtin_addressof(__x._M_unex),
|
|
std::move(_M_unex)); // might throw
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
_M_has_value = true;
|
|
__x._M_has_value = false;
|
|
}
|
|
else
|
|
{
|
|
using std::swap;
|
|
swap(_M_unex, __x._M_unex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// observers
|
|
|
|
[[nodiscard]]
|
|
constexpr explicit
|
|
operator bool() const noexcept { return _M_has_value; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool has_value() const noexcept { return _M_has_value; }
|
|
|
|
constexpr void
|
|
operator*() const noexcept { __glibcxx_assert(_M_has_value); }
|
|
|
|
constexpr void
|
|
value() const&
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return;
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
|
|
}
|
|
|
|
constexpr void
|
|
value() &&
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return;
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
|
|
}
|
|
|
|
constexpr const _Er&
|
|
error() const & noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return _M_unex;
|
|
}
|
|
|
|
constexpr _Er&
|
|
error() & noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return _M_unex;
|
|
}
|
|
|
|
constexpr const _Er&&
|
|
error() const && noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
constexpr _Er&&
|
|
error() && noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
template<typename _Gr = _Er>
|
|
constexpr _Er
|
|
error_or(_Gr&& __e) const&
|
|
{
|
|
static_assert( is_copy_constructible_v<_Er> );
|
|
static_assert( is_convertible_v<_Gr, _Er> );
|
|
|
|
if (_M_has_value)
|
|
return std::forward<_Gr>(__e);
|
|
return _M_unex;
|
|
}
|
|
|
|
template<typename _Gr = _Er>
|
|
constexpr _Er
|
|
error_or(_Gr&& __e) &&
|
|
{
|
|
static_assert( is_move_constructible_v<_Er> );
|
|
static_assert( is_convertible_v<_Gr, _Er> );
|
|
|
|
if (_M_has_value)
|
|
return std::forward<_Gr>(__e);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
// monadic operations
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, _Er&>
|
|
constexpr auto
|
|
and_then(_Fn&& __f) &
|
|
{
|
|
using _Up = __expected::__result0<_Fn>;
|
|
static_assert(__expected::__is_expected<_Up>);
|
|
static_assert(is_same_v<typename _Up::error_type, _Er>);
|
|
|
|
if (has_value())
|
|
return std::__invoke(std::forward<_Fn>(__f));
|
|
else
|
|
return _Up(unexpect, _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
|
|
constexpr auto
|
|
and_then(_Fn&& __f) const &
|
|
{
|
|
using _Up = __expected::__result0<_Fn>;
|
|
static_assert(__expected::__is_expected<_Up>);
|
|
static_assert(is_same_v<typename _Up::error_type, _Er>);
|
|
|
|
if (has_value())
|
|
return std::__invoke(std::forward<_Fn>(__f));
|
|
else
|
|
return _Up(unexpect, _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, _Er>
|
|
constexpr auto
|
|
and_then(_Fn&& __f) &&
|
|
{
|
|
using _Up = __expected::__result0<_Fn>;
|
|
static_assert(__expected::__is_expected<_Up>);
|
|
static_assert(is_same_v<typename _Up::error_type, _Er>);
|
|
|
|
if (has_value())
|
|
return std::__invoke(std::forward<_Fn>(__f));
|
|
else
|
|
return _Up(unexpect, std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, const _Er>
|
|
constexpr auto
|
|
and_then(_Fn&& __f) const &&
|
|
{
|
|
using _Up = __expected::__result0<_Fn>;
|
|
static_assert(__expected::__is_expected<_Up>);
|
|
static_assert(is_same_v<typename _Up::error_type, _Er>);
|
|
|
|
if (has_value())
|
|
return std::__invoke(std::forward<_Fn>(__f));
|
|
else
|
|
return _Up(unexpect, std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn>
|
|
constexpr auto
|
|
or_else(_Fn&& __f) &
|
|
{
|
|
using _Gr = __expected::__result<_Fn, _Er&>;
|
|
static_assert(__expected::__is_expected<_Gr>);
|
|
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
|
|
|
|
if (has_value())
|
|
return _Gr();
|
|
else
|
|
return std::__invoke(std::forward<_Fn>(__f), _M_unex);
|
|
}
|
|
|
|
template<typename _Fn>
|
|
constexpr auto
|
|
or_else(_Fn&& __f) const &
|
|
{
|
|
using _Gr = __expected::__result<_Fn, const _Er&>;
|
|
static_assert(__expected::__is_expected<_Gr>);
|
|
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
|
|
|
|
if (has_value())
|
|
return _Gr();
|
|
else
|
|
return std::__invoke(std::forward<_Fn>(__f), _M_unex);
|
|
}
|
|
|
|
template<typename _Fn>
|
|
constexpr auto
|
|
or_else(_Fn&& __f) &&
|
|
{
|
|
using _Gr = __expected::__result<_Fn, _Er&&>;
|
|
static_assert(__expected::__is_expected<_Gr>);
|
|
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
|
|
|
|
if (has_value())
|
|
return _Gr();
|
|
else
|
|
return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn>
|
|
constexpr auto
|
|
or_else(_Fn&& __f) const &&
|
|
{
|
|
using _Gr = __expected::__result<_Fn, const _Er&&>;
|
|
static_assert(__expected::__is_expected<_Gr>);
|
|
static_assert(is_same_v<typename _Gr::value_type, _Tp>);
|
|
|
|
if (has_value())
|
|
return _Gr();
|
|
else
|
|
return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, _Er&>
|
|
constexpr auto
|
|
transform(_Fn&& __f) &
|
|
{
|
|
using _Up = __expected::__result0_xform<_Fn>;
|
|
using _Res = expected<_Up, _Er>;
|
|
|
|
if (has_value())
|
|
return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
|
|
else
|
|
return _Res(unexpect, _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
|
|
constexpr auto
|
|
transform(_Fn&& __f) const &
|
|
{
|
|
using _Up = __expected::__result0_xform<_Fn>;
|
|
using _Res = expected<_Up, _Er>;
|
|
|
|
if (has_value())
|
|
return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
|
|
else
|
|
return _Res(unexpect, _M_unex);
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, _Er>
|
|
constexpr auto
|
|
transform(_Fn&& __f) &&
|
|
{
|
|
using _Up = __expected::__result0_xform<_Fn>;
|
|
using _Res = expected<_Up, _Er>;
|
|
|
|
if (has_value())
|
|
return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
|
|
else
|
|
return _Res(unexpect, std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn> requires is_constructible_v<_Er, const _Er>
|
|
constexpr auto
|
|
transform(_Fn&& __f) const &&
|
|
{
|
|
using _Up = __expected::__result0_xform<_Fn>;
|
|
using _Res = expected<_Up, _Er>;
|
|
|
|
if (has_value())
|
|
return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
|
|
else
|
|
return _Res(unexpect, std::move(_M_unex));
|
|
}
|
|
|
|
template<typename _Fn>
|
|
constexpr auto
|
|
transform_error(_Fn&& __f) &
|
|
{
|
|
using _Gr = __expected::__result_xform<_Fn, _Er&>;
|
|
using _Res = expected<_Tp, _Gr>;
|
|
|
|
if (has_value())
|
|
return _Res();
|
|
else
|
|
return _Res(__unexpect_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
_M_unex);
|
|
});
|
|
}
|
|
|
|
template<typename _Fn>
|
|
constexpr auto
|
|
transform_error(_Fn&& __f) const &
|
|
{
|
|
using _Gr = __expected::__result_xform<_Fn, const _Er&>;
|
|
using _Res = expected<_Tp, _Gr>;
|
|
|
|
if (has_value())
|
|
return _Res();
|
|
else
|
|
return _Res(__unexpect_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
_M_unex);
|
|
});
|
|
}
|
|
|
|
template<typename _Fn>
|
|
constexpr auto
|
|
transform_error(_Fn&& __f) &&
|
|
{
|
|
using _Gr = __expected::__result_xform<_Fn, _Er&&>;
|
|
using _Res = expected<_Tp, _Gr>;
|
|
|
|
if (has_value())
|
|
return _Res();
|
|
else
|
|
return _Res(__unexpect_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
std::move(_M_unex));
|
|
});
|
|
}
|
|
|
|
template<typename _Fn>
|
|
constexpr auto
|
|
transform_error(_Fn&& __f) const &&
|
|
{
|
|
using _Gr = __expected::__result_xform<_Fn, const _Er&&>;
|
|
using _Res = expected<_Tp, _Gr>;
|
|
|
|
if (has_value())
|
|
return _Res();
|
|
else
|
|
return _Res(__unexpect_inv{}, [&]() {
|
|
return std::__invoke(std::forward<_Fn>(__f),
|
|
std::move(_M_unex));
|
|
});
|
|
}
|
|
|
|
// equality operators
|
|
|
|
template<typename _Up, typename _Er2>
|
|
requires is_void_v<_Up>
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const expected<_Up, _Er2>& __y)
|
|
// FIXME: noexcept(noexcept(bool(__x.error() == __y.error())))
|
|
{
|
|
if (__x.has_value())
|
|
return __y.has_value();
|
|
else
|
|
return !__y.has_value() && bool(__x.error() == __y.error());
|
|
}
|
|
|
|
template<typename _Er2>
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const unexpected<_Er2>& __e)
|
|
// FIXME: noexcept(noexcept(bool(__x.error() == __e.error())))
|
|
{ return !__x.has_value() && bool(__x.error() == __e.error()); }
|
|
|
|
friend constexpr void
|
|
swap(expected& __x, expected& __y)
|
|
noexcept(noexcept(__x.swap(__y)))
|
|
requires requires { __x.swap(__y); }
|
|
{ __x.swap(__y); }
|
|
|
|
private:
|
|
template<typename, typename> friend class expected;
|
|
|
|
template<typename _Vp>
|
|
constexpr void
|
|
_M_assign_unex(_Vp&& __v)
|
|
{
|
|
if (_M_has_value)
|
|
{
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::forward<_Vp>(__v));
|
|
_M_has_value = false;
|
|
}
|
|
else
|
|
_M_unex = std::forward<_Vp>(__v);
|
|
}
|
|
|
|
using __in_place_inv = __expected::__in_place_inv;
|
|
using __unexpect_inv = __expected::__unexpect_inv;
|
|
|
|
template<typename _Fn>
|
|
explicit constexpr
|
|
expected(__in_place_inv, _Fn&& __fn)
|
|
: _M_void(), _M_has_value(true)
|
|
{ std::forward<_Fn>(__fn)(); }
|
|
|
|
template<typename _Fn>
|
|
explicit constexpr
|
|
expected(__unexpect_inv, _Fn&& __fn)
|
|
: _M_unex(std::forward<_Fn>(__fn)()), _M_has_value(false)
|
|
{ }
|
|
|
|
union {
|
|
struct { } _M_void;
|
|
_Er _M_unex;
|
|
};
|
|
|
|
bool _M_has_value;
|
|
};
|
|
/// @}
|
|
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace std
|
|
|
|
#endif // C++23
|
|
#endif // _GLIBCXX_EXPECTED
|