mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-01-12 00:05:41 +08:00
The following patch attempts to implement the C++26 P3378R2 - constexpr exception types paper. This is quite complicated, because most of these classes which should be constexpr-ized use solely or mostly out of line definitions in libstdc++, both for historical, code size and dual ABI reasons, so that one can throw these as exceptions between TUs with old vs. new (or vice versa) ABIs. For this reason, logic_error/runtime_error and classes derived from it have the old ABI std::string object inside of them and the exported APIs from libstdc++.so.6 ensure the right thing. Now, because new invoked during constant evaluation needs to be deleted during the same constant evaluation and can't leak into the constant expressions, I think we don't have to use COW strings under the hood (which aren't constexpr I guess because of reference counting/COW) and we can use something else, the patch uses heap allocated std::string object (where __cow_constexpr_string class has just a pointer to that). As I think we still want to hide the ugly details if !consteval in the library, the patch exports 8 __cow_string class symbols (6 existing which were previously just not exported and 2 new ones) and if !consteval calls those through extern "C" _Zmangled_name symbols. The functions are always_inline. And then logic_error etc. have for C++26 (precisely for __cpp_lib_constexpr_exceptions >= 202502L) constexpr definitions of cdtors/methods. This results in slightly larger code (a few insns at most) at runtime for C++26, e.g. instead of calling say some logic error cdtor/method with 2 arguments it calls some __cow_string one with 2 arguments but + 8 bytes pointer additions on both. The patch also removes the __throw_format_error forward declaration which apparently wasn't needed for anything as all __throw_format_error users were either in <format> or included <format> before the uses, reverts the https://gcc.gnu.org/pipermail/libstdc++/2025-July/062598.html patch and makes sure __throw_* functions (only those for exception types which the P3378R2 or P3068R5 papers made constexpr usable and there are actually constexpr/consteval uses of those) are constexpr for C++26 constexpr exceptions. The patch does that by splitting the bits/functexcept.h header: 1) bits/functexcept.h stays for the __throw_* functions which are (at least for now) never constexpr (the <ios>, <system_error>, <future> and <functional> std::exception derived classes) or are never used or never used in constexpr/consteval contexts (<exception>, <typeinfo> std::exception derived classes and std::range_error). 2) bits/new_{throw,except}.h for __throw_bad_alloc/__throw_bad_array_new_length and std::bad_alloc/std::bad_array_new_length (where <new> includes <bits/new_except.h> and <bits/new_throw.h> as well for the C++26 constexpr exceptions case) 3) for the most complicated <stdexcept> stuff, one header addition to bits/stdexcept.h one header for the __throw_logic_error etc. forward declarations, one header for the __throw_logic_error etc. definitions and one header without header guards which will depending on __glibcxx_exc_in_string include one or the other because <string> vs. <string_view> vs. <stdexcept> have heavy interdependencies 2025-12-11 Jakub Jelinek <jakub@redhat.com> PR libstdc++/121114 libstdc++-v3/ * include/bits/version.def: Implement C++26 P3378R2 - constexpr exception types. (constexpr_exceptions): Change value from 1 to 202502, remove no_stdname and TODO comments. * include/bits/version.h: Regenerate. * src/c++11/cow-stdexcept.cc (__cow_string(const char*)): New ctor. (__cow_string::c_str()): New method. * config/abi/pre/gnu.ver (GLIBCXX_3.4.35): Export 8 __cow_string symbols. * include/bits/new_except.h: New file. * include/bits/new_throw.h: New file. * include/bits/stdexcept_throw.h: New file. * include/bits/stdexcept_throwdef.h: New file. * include/bits/stdexcept_throwfwd.h: New file. * include/std/stdexcept: Include bits/stdexcept_except.h and move everything after <string> include except for std::range_error into include/bits/stdexcept_except.h. (std::range_error): If __cpp_lib_constexpr_exceptions >= 202502L make all cdtors and methods constexpr. * include/bits/stdexcept_except.h: New file. * include/std/optional (__glibcxx_want_constexpr_exceptions): Define before including bits/version.h. (bad_optional_access::what): Make constexpr for __cpp_lib_constexpr_exceptions >= 202502L. (__throw_bad_optional_access): Likewise. * include/std/expected (__glibcxx_want_constexpr_exceptions): Define before including bits/version.h. (bad_expected_access): Make cdtors and all methods constexpr for __cpp_lib_constexpr_exceptions >= 202502L. * include/std/format (__glibcxx_want_constexpr_exceptions): Define before including bits/version.h. (_GLIBCXX_CONSTEXPR_FORMAT_ERROR): Define and undef later. (format_error): Use _GLIBCXX_CONSTEXPR_FORMAT_ERROR on ctors. * include/std/variant (__glibcxx_want_constexpr_exceptions): Define before including bits/version.h. (_GLIBCXX_CONSTEXPR_BAD_VARIANT_ACCESS): Define and undef later. (bad_variant_access): Use it on ctors and what() method. (__throw_bad_variant_access): Use it here too. * testsuite/18_support/exception/version.cc: Adjust expected __cpp_lib_constexpr_exceptions value. * testsuite/19_diagnostics/runtime_error/constexpr.cc: New test. * testsuite/19_diagnostics/headers/stdexcept/version.cc: New test. * testsuite/19_diagnostics/logic_error/constexpr.cc: New test. * testsuite/20_util/expected/observers.cc (test_value_throw): Change return type to bool from void, return true at the end, add test to dereference what() first character. Make it constexpr for __cpp_lib_constexpr_exceptions >= 202502L and add static_assert. * testsuite/20_util/expected/version.cc: Add tests for __cpp_lib_constexpr_exceptions value. * testsuite/20_util/variant/constexpr.cc: For __cpp_lib_constexpr_exceptions >= 202502L include <string>. (test_get): New function if __cpp_lib_constexpr_exceptions >= 202502L, assert calling it is true. * testsuite/20_util/variant/version.cc: Add tests for __cpp_lib_constexpr_exceptions value. * testsuite/20_util/optional/constexpr/observers/3.cc: Include testsuite_hooks.h. (eat, test01): New functions. Assert test01() is true. * testsuite/20_util/optional/version.cc: Add tests for __cpp_lib_constexpr_exceptions value. * include/std/future: Add #include <bits/functexcept.h>. * include/std/shared_mutex: Include <bits/new_throw.h>. * include/std/flat_map: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/std/syncstream: Remove <bits/functexcept.h> include. * include/std/flat_set: Likewise. * include/std/bitset: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/std/string_view: Don't include <bits/functexcept.h>, include <bits/stdexcept_throw.h> early if __glibcxx_exc_in_string is not defined and include <bits/stdexcept_throw.h> at the end of the header again if __glibcxx_exc_in_string is 2 and C++26 constexpr exceptions are enabled. (__glibcxx_exc_in_string): Define if __glibcxx_exc_in_string wasn't defined before including <bits/stdexcept_throw.h>. * include/std/array: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/std/inplace_vector: Likewise. * include/std/string: Include <bits/stdexcept_except.h> and <bits/stdexcept_throw.h> after bits/basic_string.tcc include if C++26 constexpr exceptions are enabled and include <bits/stdexcept_throw.h> instead of <bits/functexcept.h> early. (__glibcxx_exc_in_string): Define early to 1, undefine at the end. * include/std/deque: Include <bits/stdexcept_throw.h>. * include/bits/new_allocator.h: Include <bits/new_throw.h> instead of <bits/functexcept.h>. * include/bits/stl_algobase.h: Remove <bits/functexcept.h> include. * include/bits/stl_vector.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/bits/memory_resource.h: Include <bits/new_throw.h> instead of <bits/functexcept.h>. * include/bits/functexcept.h: Guard everything after includes with #if _GLIBCXX_HOSTED. (__throw_bad_alloc, __throw_bad_array_new_length, __throw_logic_error, __throw_domain_error, __throw_invalid_argument, __throw_length_error, __throw_out_of_range, __throw_out_of_range_fmt, __throw_runtime_error, __throw_overflow_error, __throw_underflow_error): Move declarations to other headers - <bits/new_throw.h> and <bits/stdexcept_throwfwd.h>. * include/bits/stl_map.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/bits/hashtable_policy.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/bits/formatfwd.h (std::__throw_format_error): Remove declaration. * include/bits/specfun.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/bits/basic_ios.h: Include <bits/functexcept.h>. * include/bits/locale_classes.h: Likewise. * include/tr1/cmath: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/tr1/memory: Remove <bits/functexcept.h> include. * include/tr1/array: Include <bits/stdexcept_throw.h>. * include/ext/vstring_util.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/ext/bitmap_allocator.h: Include <bits/new_throw.h> instead of <bits/functexcept.h>. * include/ext/mt_allocator.h: Likewise. * include/ext/malloc_allocator.h: Likewise. * include/ext/debug_allocator.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/ext/concurrence.h: Include <bits/exception_defines.h> instead of <bits/functexcept.h>. * include/ext/throw_allocator.h: Include <bits/new_throw.h> and <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/ext/string_conversions.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/ext/pool_allocator.h: Include <bits/new_throw.h> instead of <bits/functexcept.h>. * include/ext/ropeimpl.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * include/tr2/dynamic_bitset: Likewise. * include/experimental/optional: Include <bits/exception_defines.h> instead of <bits/functexcept.h>. * include/Makefile.am (bits_freestanding): Add ${bits_srcdir}/{new,stdexcept}_{except,throw}.h and ${bits_srcdir}/stdexcept_throw{fwd,def}.h. * include/Makefile.in: Regenerate. * src/c++17/floating_from_chars.cc: Remove <bits/functexcept.h> include. * src/c++11/regex.cc: Likewise. * src/c++11/functexcept.cc: Likewise. * src/c++11/snprintf_lite.cc: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * src/c++11/thread.cc: Include <bits/functexcept.h>. * testsuite/util/testsuite_hooks.h: Include <bits/stdexcept_throw.h> instead of <bits/functexcept.h>. * testsuite/util/io/verified_cmd_line_input.cc: Include <bits/exception_defines.h> instead of <bits/functexcept.h>. * testsuite/20_util/allocator/105975.cc: Expect different diagnostics for C++26. * testsuite/23_containers/inplace_vector/access/capacity.cc: Remove #error, guard if consteval { return; } with #ifndef __cpp_lib_constexpr_exceptions. * testsuite/23_containers/inplace_vector/access/elem.cc: Likewise. * testsuite/23_containers/inplace_vector/cons/1.cc: Likewise. * testsuite/23_containers/inplace_vector/cons/from_range.cc: Likewise. * testsuite/23_containers/inplace_vector/modifiers/single_insert.cc: Likewise. * testsuite/23_containers/inplace_vector/modifiers/assign.cc: Likewise. * testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc: Likewise. * libsupc++/new: Include <bits/new_except.h>. (std::bad_alloc, std::bad_array_new_length): Move defintion to <bits/new_except.h>. libgomp/ * omp.h.in: Include <bits/new_throw.h> instead of <bits/functexcept.h>. gcc/testsuite/ * g++.dg/tree-ssa/pr110819.C: Guard scan-tree-dump-not delete on c++23_down and add comment explaining why C++26 fails that. * g++.dg/tree-ssa/pr96945.C: Likewise. * g++.dg/tree-ssa/pr109442.C: Likewise. * g++.dg/tree-ssa/pr116868.C: Likewise. * g++.dg/tree-ssa/pr58483.C: Likewise.
1946 lines
53 KiB
C++
1946 lines
53 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>>;
|
|
}
|
|
/// @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
|
|
|
|
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 = 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
|
|
|
|
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&
|
|
{
|
|
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
|