Files
gcc/libstdc++-v3/include/std/expected
Jonathan Wakely 57f571f728 libstdc++: Make std::expected trivially copy/move assignable (LWG 4026)
This is the subject of two NB comments on C++26 which seem likely to be
approved. We're allowed to make this change as QoI anyway, even if it
isn't approved for the standard, and it should apply to C++23 as well to
avoid ABI changes between C++23 and C++26.

As shown in the updates to the test, defaulted special members can have
noexcept(false) even if they would be noexcept(true) by default. The new
defaulted operator= overloads added by this commit have conditional
noexcept-specifiers that match the conditions of the non-trivial
assignments, propagating any noexcept(false) on trivial special members
of the T and E types. We could strengthen the noexcept for the trivial
operators, but propagating the conditions from the underlying types is
probably what users expect, if they've bothered to put noexcept(false)
on their defaulted special members.

libstdc++-v3/ChangeLog:

	* include/std/expected (__expected::__trivially_replaceable)
	(__expected::__usable_for_assign)
	(__expected::__usable_for_trivial_assign)
	(__expected::__can_reassign_type): New concepts.
	(expected::operator=): Adjust constraints
	on existing overloads and add defaulted overload.
	(expected<cv void, E>::operator=): Likewise.
	* testsuite/20_util/expected/requirements.cc: Check for trivial
	and nothrow properties of assignments.
2026-01-29 20:17:40 +00:00

2011 lines
56 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
#ifdef _GLIBCXX_SYSHDR
#pragma GCC system_header
#endif
#define __glibcxx_want_expected
#define __glibcxx_want_freestanding_expected
#define __glibcxx_want_constrained_equality
#define __glibcxx_want_constexpr_exceptions
#include <bits/version.h>
#ifdef __cpp_lib_expected // C++ >= 23 && __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
* @{
*/
/// 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;
#if __cpp_lib_constexpr_exceptions >= 202502L
#define _GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS constexpr
#else
#define _GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS
#endif
template<>
class bad_expected_access<void> : public exception
{
protected:
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS bad_expected_access() noexcept { }
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS
bad_expected_access(const bad_expected_access&) noexcept = default;
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS
bad_expected_access(bad_expected_access&&) noexcept = default;
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS
bad_expected_access& operator=(const bad_expected_access&) noexcept = default;
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS
bad_expected_access& operator=(bad_expected_access&&) noexcept = default;
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS
~bad_expected_access() = default;
public:
[[nodiscard]]
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS 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:
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS explicit
bad_expected_access(_Er __e) : _M_unex(std::move(__e)) { }
// XXX const char* what() const noexcept override;
[[nodiscard]]
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS _Er&
error() & noexcept
{ return _M_unex; }
[[nodiscard]]
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS const _Er&
error() const & noexcept
{ return _M_unex; }
[[nodiscard]]
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS _Er&&
error() && noexcept
{ return std::move(_M_unex); }
[[nodiscard]]
_GLIBCXX_CONSTEXPR_BAD_EXPECTED_ACCESS 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();
}
}
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3836. std::expected<bool, E1> conversion constructor
// expected(const expected<U, G>&) should take precedence over
// expected(U&&) with operator bool
// If T is cv bool, remove_cvref_t<U> is not a specialization of expected.
template<typename _Tp, typename _Up>
concept __not_constructing_bool_from_expected
= ! is_same_v<remove_cv_t<_Tp>, bool>
|| ! __is_expected<remove_cvref_t<_Up>>;
template<typename _Tp, typename _Up = remove_cvref_t<_Tp>>
concept __trivially_replaceable
= is_trivially_constructible_v<_Up, _Tp>
&& is_trivially_assignable_v<_Up&, _Tp>
&& is_trivially_destructible_v<_Up>;
template<typename _Tp, typename _Up = remove_cvref_t<_Tp>>
concept __usable_for_assign
= is_constructible_v<_Up, _Tp> && is_assignable_v<_Up&, _Tp>;
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 4026. Assignment operators of std::expected should propagate triviality
template<typename _Tp>
concept __usable_for_trivial_assign
= __trivially_replaceable<_Tp> && __usable_for_assign<_Tp>;
// For copy/move assignment to replace T with E (or vice versa)
// we require at least one of them to be nothrow move constructible.
template<typename _Tp, typename _Er>
concept __can_reassign_type
= is_nothrow_move_constructible_v<_Tp>
|| is_nothrow_move_constructible_v<_Er>;
}
/// @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> );
// If T is not cv bool, converts-from-any-cvref<T, expected<U, G>> and
// is_constructible<unexpected<E>, cv expected<U, G> ref-qual> are false.
template<typename _Up, typename _Gr, typename _Unex = unexpected<_Er>,
typename = remove_cv_t<_Tp>>
static constexpr bool __cons_from_expected
= __or_v<is_constructible<_Tp, expected<_Up, _Gr>&>,
is_constructible<_Tp, expected<_Up, _Gr>>,
is_constructible<_Tp, const expected<_Up, _Gr>&>,
is_constructible<_Tp, const expected<_Up, _Gr>>,
is_convertible<expected<_Up, _Gr>&, _Tp>,
is_convertible<expected<_Up, _Gr>, _Tp>,
is_convertible<const expected<_Up, _Gr>&, _Tp>,
is_convertible<const expected<_Up, _Gr>, _Tp>,
is_constructible<_Unex, expected<_Up, _Gr>&>,
is_constructible<_Unex, expected<_Up, _Gr>>,
is_constructible<_Unex, const expected<_Up, _Gr>&>,
is_constructible<_Unex, const expected<_Up, _Gr>>
>;
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// If t is cv bool, we know it can be constructed from expected<U, G>,
// but we don't want to cause the expected(U&&) constructor to be used,
// so we only check the is_constructible<unexpected<E>, ...> cases.
template<typename _Up, typename _Gr, typename _Unex>
static constexpr bool __cons_from_expected<_Up, _Gr, _Unex, bool>
= __or_v<is_constructible<_Unex, expected<_Up, _Gr>&>,
is_constructible<_Unex, expected<_Up, _Gr>>,
is_constructible<_Unex, const expected<_Up, _Gr>&>,
is_constructible<_Unex, const expected<_Up, _Gr>>
>;
template<typename _Up, typename _Gr>
constexpr static bool __explicit_conv
= __or_v<__not_<is_convertible<_Up, _Tp>>,
__not_<is_convertible<_Gr, _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 = remove_cv_t<_Tp>>
requires (!is_same_v<remove_cvref_t<_Up>, expected>)
&& (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
&& (!is_same_v<remove_cvref_t<_Up>, unexpect_t>)
&& is_constructible_v<_Tp, _Up>
&& (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
&& __expected::__not_constructing_bool_from_expected<_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
// Deleted copy assignment, when constraints not met for other overloads
expected& operator=(const expected&) = delete;
// Trivial copy assignment
expected&
operator=(const expected&)
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
is_nothrow_copy_constructible<_Er>,
is_nothrow_copy_assignable<_Tp>,
is_nothrow_copy_assignable<_Er>>)
requires __expected::__usable_for_trivial_assign<const _Tp&>
&& __expected::__usable_for_trivial_assign<const _Er&>
&& __expected::__can_reassign_type<_Tp, _Er>
= default;
// Non-trivial copy assignment
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 __expected::__usable_for_assign<const _Tp&>
&& __expected::__usable_for_assign<const _Er&>
&& __expected::__can_reassign_type<_Tp, _Er>
{
if (__x._M_has_value)
this->_M_assign_val(__x._M_val);
else
this->_M_assign_unex(__x._M_unex);
return *this;
}
// Trivial move assignment
expected&
operator=(expected&&)
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
is_nothrow_move_constructible<_Er>,
is_nothrow_move_assignable<_Tp>,
is_nothrow_move_assignable<_Er>>)
requires __expected::__usable_for_trivial_assign<_Tp&&>
&& __expected::__usable_for_trivial_assign<_Er&&>
&& __expected::__can_reassign_type<_Tp, _Er>
= default;
// Non-trivial move assignment
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 __expected::__usable_for_assign<_Tp&&>
&& __expected::__usable_for_assign<_Er&&>
&& __expected::__can_reassign_type<_Tp, _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 = remove_cv_t<_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 &
{
static_assert( is_copy_constructible_v<_Er> );
if (_M_has_value) [[likely]]
return _M_val;
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
}
constexpr _Tp&
value() &
{
static_assert( is_copy_constructible_v<_Er> );
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 &&
{
static_assert( is_copy_constructible_v<_Er> );
static_assert( is_constructible_v<_Er, const _Er&&> );
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() &&
{
static_assert( is_copy_constructible_v<_Er> );
static_assert( is_constructible_v<_Er, _Er&&> );
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);
}
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 4406. value_or return statement is inconsistent with Mandates
template<typename _Up = remove_cv_t<_Tp>>
constexpr remove_cv_t<_Tp>
value_or(_Up&& __v) const &
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
is_nothrow_convertible<_Up, _Tp>>)
{
using _Xp = remove_cv_t<_Tp>;
static_assert( is_convertible_v<const _Tp&, _Xp> );
static_assert( is_convertible_v<_Up, _Tp> );
if (_M_has_value)
return _M_val;
return std::forward<_Up>(__v);
}
template<typename _Up = remove_cv_t<_Tp>>
constexpr remove_cv_t<_Tp>
value_or(_Up&& __v) &&
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
is_nothrow_convertible<_Up, _Tp>>)
{
using _Xp = remove_cv_t<_Tp>;
static_assert( is_convertible_v<_Tp, _Xp> );
static_assert( is_convertible_v<_Up, _Xp> );
if (_M_has_value)
return std::move(_M_val);
return 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>,
"the function passed to std::expected<T, E>::and_then "
"must return a std::expected");
static_assert(is_same_v<typename _Up::error_type, _Er>,
"the function passed to std::expected<T, E>::and_then "
"must return a std::expected with the same error_type");
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>,
"the function passed to std::expected<T, E>::and_then "
"must return a std::expected");
static_assert(is_same_v<typename _Up::error_type, _Er>,
"the function passed to std::expected<T, E>::and_then "
"must return a std::expected with the same error_type");
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>,
"the function passed to std::expected<T, E>::and_then "
"must return a std::expected");
static_assert(is_same_v<typename _Up::error_type, _Er>,
"the function passed to std::expected<T, E>::and_then "
"must return a std::expected with the same error_type");
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>,
"the function passed to std::expected<T, E>::and_then "
"must return a std::expected");
static_assert(is_same_v<typename _Up::error_type, _Er>,
"the function passed to std::expected<T, E>::and_then "
"must return a std::expected with the same error_type");
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>,
"the function passed to std::expected<T, E>::or_else "
"must return a std::expected");
static_assert(is_same_v<typename _Gr::value_type, _Tp>,
"the function passed to std::expected<T, E>::or_else "
"must return a std::expected with the same value_type");
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>,
"the function passed to std::expected<T, E>::or_else "
"must return a std::expected");
static_assert(is_same_v<typename _Gr::value_type, _Tp>,
"the function passed to std::expected<T, E>::or_else "
"must return a std::expected with the same value_type");
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>,
"the function passed to std::expected<T, E>::or_else "
"must return a std::expected");
static_assert(is_same_v<typename _Gr::value_type, _Tp>,
"the function passed to std::expected<T, E>::or_else "
"must return a std::expected with the same value_type");
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>,
"the function passed to std::expected<T, E>::or_else "
"must return a std::expected");
static_assert(is_same_v<typename _Gr::value_type, _Tp>,
"the function passed to std::expected<T, E>::or_else "
"must return a std::expected with the same value_type");
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>)
&& requires (const _Tp& __t, const _Up& __u,
const _Er& __e, const _Er2& __e2) {
{ __t == __u } -> convertible_to<bool>;
{ __e == __e2 } -> convertible_to<bool>;
}
[[nodiscard]]
friend constexpr bool
operator==(const expected& __x, const expected<_Up, _Er2>& __y)
noexcept(noexcept(bool(*__x == *__y))
&& noexcept(bool(__x.error() == __y.error())))
{
if (__x.has_value() != __y.has_value())
return false;
if (__x.has_value())
return *__x == *__y;
return __x.error() == __y.error();
}
template<typename _Up, same_as<_Tp> _Vp>
requires (!__expected::__is_expected<_Up>)
&& requires (const _Tp& __t, const _Up& __u) {
{ __t == __u } -> convertible_to<bool>;
}
[[nodiscard]]
friend constexpr bool
operator==(const expected<_Vp, _Er>& __x, const _Up& __v)
noexcept(noexcept(bool(*__x == __v)))
{
if (__x.has_value())
return *__x == __v;
return false;
}
template<typename _Er2>
requires requires (const _Er& __e, const _Er2& __e2) {
{ __e == __e2 } -> convertible_to<bool>;
}
[[nodiscard]]
friend constexpr bool
operator==(const expected& __x, const unexpected<_Er2>& __e)
noexcept(noexcept(bool(__x.error() == __e.error())))
{
if (!__x.has_value())
return __x.error() == __e.error();
return false;
}
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 {
remove_cv_t<_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
// Deleted copy assignment, when constraints not met for other overloads
expected& operator=(const expected&) = delete;
// Trivial copy assignment
expected&
operator=(const expected&)
noexcept(__and_v<is_nothrow_copy_constructible<_Er>,
is_nothrow_copy_assignable<_Er>>)
requires __expected::__usable_for_trivial_assign<const _Er&>
= default;
// Non-trivial copy assignment
constexpr expected&
operator=(const expected& __x)
noexcept(__and_v<is_nothrow_copy_constructible<_Er>,
is_nothrow_copy_assignable<_Er>>)
requires __expected::__usable_for_assign<const _Er&>
{
if (__x._M_has_value)
emplace();
else
_M_assign_unex(__x._M_unex);
return *this;
}
// Trivial move assignment
expected&
operator=(expected&&)
noexcept(__and_v<is_nothrow_move_constructible<_Er>,
is_nothrow_move_assignable<_Er>>)
requires __expected::__usable_for_trivial_assign<_Er&&>
= default;
// Non-trivial move assignment
constexpr expected&
operator=(expected&& __x)
noexcept(__and_v<is_nothrow_move_constructible<_Er>,
is_nothrow_move_assignable<_Er>>)
requires __expected::__usable_for_assign<_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&
{
static_assert( is_copy_constructible_v<_Er> );
if (_M_has_value) [[likely]]
return;
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
}
constexpr void
value() &&
{
static_assert( is_copy_constructible_v<_Er> );
static_assert( is_move_constructible_v<_Er> );
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>
&& requires (const _Er& __e, const _Er2& __e2) {
{ __e == __e2 } -> convertible_to<bool>;
}
[[nodiscard]]
friend constexpr bool
operator==(const expected& __x, const expected<_Up, _Er2>& __y)
noexcept(noexcept(bool(__x.error() == __y.error())))
{
if (__x.has_value() != __y.has_value())
return false;
if (__x.has_value())
return true;
return __x.error() == __y.error();
}
template<typename _Er2>
requires requires (const _Er& __e, const _Er2& __e2) {
{ __e == __e2 } -> convertible_to<bool>;
}
[[nodiscard]]
friend constexpr bool
operator==(const expected& __x, const unexpected<_Er2>& __e)
noexcept(noexcept(bool(__x.error() == __e.error())))
{
if (!__x.has_value())
return __x.error() == __e.error();
return false;
}
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 // __cpp_lib_expected
#endif // _GLIBCXX_EXPECTED