mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 12:00:11 -05:00
LWG 3843 adds some type requirements to std::expected::value to ensure
that it can correctly copy the error value if it needs to throw an
exception. We don't need to do anything to enforce that, because it will
already be ill-formed if the type can't be copied. The issue also makes
a small drive-by fix to ensure that a const E& is copied from the
non-const value()& overload, which this change implements.
libstdc++-v3/ChangeLog:
* include/std/expected (expected::value() &): Use const lvalue
for unex member passed to bad_expected_access constructor, as
per LWG 3843.
(cherry picked from commit ce39714a1c)
1241 lines
33 KiB
C++
1241 lines
33 KiB
C++
// <expected> -*- C++ -*-
|
|
|
|
// Copyright The GNU Toolchain Authors.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file include/expected
|
|
* This is a Standard C++ Library header.
|
|
*/
|
|
|
|
#ifndef _GLIBCXX_EXPECTED
|
|
#define _GLIBCXX_EXPECTED
|
|
|
|
#pragma GCC system_header
|
|
|
|
#if __cplusplus > 202002L && __cpp_concepts >= 202002L
|
|
|
|
#include <initializer_list>
|
|
#include <bits/exception.h> // exception
|
|
#include <bits/stl_construct.h> // construct_at
|
|
#include <bits/utility.h> // in_place_t
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
|
|
/**
|
|
* @defgroup expected_values Expected values
|
|
* @addtogroup utilities
|
|
* @since C++23
|
|
* @{
|
|
*/
|
|
|
|
#define __cpp_lib_expected 202202L
|
|
|
|
/// Discriminated union that holds an expected value or an error value.
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
template<typename _Tp, typename _Er>
|
|
class expected;
|
|
|
|
/// Wrapper type used to pass an error value to a `std::expected`.
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
template<typename _Er>
|
|
class unexpected;
|
|
|
|
/// Exception thrown by std::expected when the value() is not present.
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
template<typename _Er>
|
|
class bad_expected_access;
|
|
|
|
template<>
|
|
class bad_expected_access<void> : public exception
|
|
{
|
|
protected:
|
|
bad_expected_access() noexcept { }
|
|
bad_expected_access(const bad_expected_access&) = default;
|
|
bad_expected_access(bad_expected_access&&) = default;
|
|
bad_expected_access& operator=(const bad_expected_access&) = default;
|
|
bad_expected_access& operator=(bad_expected_access&&) = default;
|
|
~bad_expected_access() = default;
|
|
|
|
public:
|
|
|
|
[[nodiscard]]
|
|
const char*
|
|
what() const noexcept override
|
|
{ return "bad access to std::expected without expected value"; }
|
|
};
|
|
|
|
template<typename _Er>
|
|
class bad_expected_access : public bad_expected_access<void> {
|
|
public:
|
|
explicit
|
|
bad_expected_access(_Er __e) : _M_unex(std::move(__e)) { }
|
|
|
|
// XXX const char* what() const noexcept override;
|
|
|
|
[[nodiscard]]
|
|
_Er&
|
|
error() & noexcept
|
|
{ return _M_unex; }
|
|
|
|
[[nodiscard]]
|
|
const _Er&
|
|
error() const & noexcept
|
|
{ return _M_unex; }
|
|
|
|
[[nodiscard]]
|
|
_Er&&
|
|
error() && noexcept
|
|
{ return std::move(_M_unex); }
|
|
|
|
[[nodiscard]]
|
|
const _Er&&
|
|
error() const && noexcept
|
|
{ return std::move(_M_unex); }
|
|
|
|
private:
|
|
_Er _M_unex;
|
|
};
|
|
|
|
/// Tag type for constructing unexpected values in a std::expected
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
struct unexpect_t
|
|
{
|
|
explicit unexpect_t() = default;
|
|
};
|
|
|
|
/// Tag for constructing unexpected values in a std::expected
|
|
/**
|
|
* @since C++23
|
|
*/
|
|
inline constexpr unexpect_t unexpect{};
|
|
|
|
/// @cond undoc
|
|
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 _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>);
|
|
}
|
|
/// @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 undoc
|
|
namespace __expected
|
|
{
|
|
template<typename _Tp>
|
|
struct _Guard
|
|
{
|
|
static_assert( is_nothrow_move_constructible_v<_Tp> );
|
|
|
|
constexpr explicit
|
|
_Guard(_Tp& __x)
|
|
: _M_guarded(__builtin_addressof(__x)), _M_tmp(std::move(__x)) // nothrow
|
|
{ std::destroy_at(_M_guarded); }
|
|
|
|
constexpr
|
|
~_Guard()
|
|
{
|
|
if (_M_guarded) [[unlikely]]
|
|
std::construct_at(_M_guarded, std::move(_M_tmp));
|
|
}
|
|
|
|
_Guard(const _Guard&) = delete;
|
|
_Guard& operator=(const _Guard&) = delete;
|
|
|
|
constexpr _Tp&&
|
|
release() noexcept
|
|
{
|
|
_M_guarded = nullptr;
|
|
return std::move(_M_tmp);
|
|
}
|
|
|
|
private:
|
|
_Tp* _M_guarded;
|
|
_Tp _M_tmp;
|
|
};
|
|
|
|
// reinit-expected helper from [expected.object.assign]
|
|
template<typename _Tp, typename _Up, typename _Vp>
|
|
constexpr void
|
|
__reinit(_Tp* __newval, _Up* __oldval, _Vp&& __arg)
|
|
noexcept(is_nothrow_constructible_v<_Tp, _Vp>)
|
|
{
|
|
if constexpr (is_nothrow_constructible_v<_Tp, _Vp>)
|
|
{
|
|
std::destroy_at(__oldval);
|
|
std::construct_at(__newval, std::forward<_Vp>(__arg));
|
|
}
|
|
else if constexpr (is_nothrow_move_constructible_v<_Tp>)
|
|
{
|
|
_Tp __tmp(std::forward<_Vp>(__arg)); // might throw
|
|
std::destroy_at(__oldval);
|
|
std::construct_at(__newval, std::move(__tmp));
|
|
}
|
|
else
|
|
{
|
|
_Guard<_Up> __guard(*__oldval);
|
|
std::construct_at(__newval, std::forward<_Vp>(__arg)); // might throw
|
|
__guard.release();
|
|
}
|
|
}
|
|
}
|
|
/// @endcond
|
|
|
|
template<typename _Tp, typename _Er>
|
|
class expected
|
|
{
|
|
static_assert( ! is_reference_v<_Tp> );
|
|
static_assert( ! is_function_v<_Tp> );
|
|
static_assert( ! is_same_v<remove_cv_t<_Tp>, in_place_t> );
|
|
static_assert( ! is_same_v<remove_cv_t<_Tp>, unexpect_t> );
|
|
static_assert( ! __expected::__is_unexpected<remove_cv_t<_Tp>> );
|
|
static_assert( __expected::__can_be_unexpected<_Er> );
|
|
|
|
template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
|
|
static constexpr bool __cons_from_expected
|
|
= __or_v<is_constructible<_Tp, expected<_Up, _Err>&>,
|
|
is_constructible<_Tp, expected<_Up, _Err>>,
|
|
is_constructible<_Tp, const expected<_Up, _Err>&>,
|
|
is_constructible<_Tp, const expected<_Up, _Err>>,
|
|
is_convertible<expected<_Up, _Err>&, _Tp>,
|
|
is_convertible<expected<_Up, _Err>, _Tp>,
|
|
is_convertible<const expected<_Up, _Err>&, _Tp>,
|
|
is_convertible<const expected<_Up, _Err>, _Tp>,
|
|
is_constructible<_Unex, expected<_Up, _Err>&>,
|
|
is_constructible<_Unex, expected<_Up, _Err>>,
|
|
is_constructible<_Unex, const expected<_Up, _Err>&>,
|
|
is_constructible<_Unex, const expected<_Up, _Err>>
|
|
>;
|
|
|
|
template<typename _Up, typename _Err>
|
|
constexpr static bool __explicit_conv
|
|
= __or_v<__not_<is_convertible<_Up, _Tp>>,
|
|
__not_<is_convertible<_Err, _Er>>
|
|
>;
|
|
|
|
public:
|
|
using value_type = _Tp;
|
|
using error_type = _Er;
|
|
using unexpected_type = unexpected<_Er>;
|
|
|
|
template<typename _Up>
|
|
using rebind = expected<_Up, error_type>;
|
|
|
|
constexpr
|
|
expected()
|
|
noexcept(is_nothrow_default_constructible_v<_Tp>)
|
|
requires is_default_constructible_v<_Tp>
|
|
: _M_val(), _M_has_value(true)
|
|
{ }
|
|
|
|
expected(const expected&) = default;
|
|
|
|
constexpr
|
|
expected(const expected& __x)
|
|
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
|
|
is_nothrow_copy_constructible<_Er>>)
|
|
requires is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Er>
|
|
&& (!is_trivially_copy_constructible_v<_Tp>
|
|
|| !is_trivially_copy_constructible_v<_Er>)
|
|
: _M_has_value(__x._M_has_value)
|
|
{
|
|
if (_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_val), __x._M_val);
|
|
else
|
|
std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
|
|
}
|
|
|
|
expected(expected&&) = default;
|
|
|
|
constexpr
|
|
expected(expected&& __x)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
|
is_nothrow_move_constructible<_Er>>)
|
|
requires is_move_constructible_v<_Tp> && is_move_constructible_v<_Er>
|
|
&& (!is_trivially_move_constructible_v<_Tp>
|
|
|| !is_trivially_move_constructible_v<_Er>)
|
|
: _M_has_value(__x._M_has_value)
|
|
{
|
|
if (_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_val),
|
|
std::move(__x)._M_val);
|
|
else
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x)._M_unex);
|
|
}
|
|
|
|
template<typename _Up, typename _Gr>
|
|
requires is_constructible_v<_Tp, const _Up&>
|
|
&& is_constructible_v<_Er, const _Gr&>
|
|
&& (!__cons_from_expected<_Up, _Gr>)
|
|
constexpr explicit(__explicit_conv<const _Up&, const _Gr&>)
|
|
expected(const expected<_Up, _Gr>& __x)
|
|
noexcept(__and_v<is_nothrow_constructible<_Tp, const _Up&>,
|
|
is_nothrow_constructible<_Er, const _Gr&>>)
|
|
: _M_has_value(__x._M_has_value)
|
|
{
|
|
if (_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_val), __x._M_val);
|
|
else
|
|
std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
|
|
}
|
|
|
|
template<typename _Up, typename _Gr>
|
|
requires is_constructible_v<_Tp, _Up>
|
|
&& is_constructible_v<_Er, _Gr>
|
|
&& (!__cons_from_expected<_Up, _Gr>)
|
|
constexpr explicit(__explicit_conv<_Up, _Gr>)
|
|
expected(expected<_Up, _Gr>&& __x)
|
|
noexcept(__and_v<is_nothrow_constructible<_Tp, _Up>,
|
|
is_nothrow_constructible<_Er, _Gr>>)
|
|
: _M_has_value(__x._M_has_value)
|
|
{
|
|
if (_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_val),
|
|
std::move(__x)._M_val);
|
|
else
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x)._M_unex);
|
|
}
|
|
|
|
template<typename _Up = _Tp>
|
|
requires (!is_same_v<remove_cvref_t<_Up>, expected>)
|
|
&& (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
|
|
&& (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
|
|
&& is_constructible_v<_Tp, _Up>
|
|
constexpr explicit(!is_convertible_v<_Up, _Tp>)
|
|
expected(_Up&& __v)
|
|
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
|
|
: _M_val(std::forward<_Up>(__v)), _M_has_value(true)
|
|
{ }
|
|
|
|
template<typename _Gr = _Er>
|
|
requires is_constructible_v<_Er, const _Gr&>
|
|
constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
|
|
expected(const unexpected<_Gr>& __u)
|
|
noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
|
|
: _M_unex(__u.error()), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename _Gr = _Er>
|
|
requires is_constructible_v<_Er, _Gr>
|
|
constexpr explicit(!is_convertible_v<_Gr, _Er>)
|
|
expected(unexpected<_Gr>&& __u)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Gr>)
|
|
: _M_unex(std::move(__u).error()), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename... _Args>
|
|
requires is_constructible_v<_Tp, _Args...>
|
|
constexpr explicit
|
|
expected(in_place_t, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
|
|
: _M_val(std::forward<_Args>(__args)...), _M_has_value(true)
|
|
{ }
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>
|
|
constexpr explicit
|
|
expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&,
|
|
_Args...>)
|
|
: _M_val(__il, std::forward<_Args>(__args)...), _M_has_value(true)
|
|
{ }
|
|
|
|
template<typename... _Args>
|
|
requires is_constructible_v<_Er, _Args...>
|
|
constexpr explicit
|
|
expected(unexpect_t, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Args...>)
|
|
: _M_unex(std::forward<_Args>(__args)...), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...>
|
|
constexpr explicit
|
|
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&,
|
|
_Args...>)
|
|
: _M_unex(__il, std::forward<_Args>(__args)...), _M_has_value(false)
|
|
{ }
|
|
|
|
constexpr ~expected() = default;
|
|
|
|
constexpr ~expected()
|
|
requires (!is_trivially_destructible_v<_Tp>)
|
|
|| (!is_trivially_destructible_v<_Er>)
|
|
{
|
|
if (_M_has_value)
|
|
std::destroy_at(__builtin_addressof(_M_val));
|
|
else
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
}
|
|
|
|
// assignment
|
|
|
|
expected& operator=(const expected&) = delete;
|
|
|
|
constexpr expected&
|
|
operator=(const expected& __x)
|
|
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
|
|
is_nothrow_copy_constructible<_Er>,
|
|
is_nothrow_copy_assignable<_Tp>,
|
|
is_nothrow_copy_assignable<_Er>>)
|
|
requires is_copy_assignable_v<_Tp> && is_copy_constructible_v<_Tp>
|
|
&& is_copy_assignable_v<_Er> && is_copy_constructible_v<_Er>
|
|
&& (is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
{
|
|
if (__x._M_has_value)
|
|
this->_M_assign_val(__x._M_val);
|
|
else
|
|
this->_M_assign_unex(__x._M_unex);
|
|
return *this;
|
|
}
|
|
|
|
constexpr expected&
|
|
operator=(expected&& __x)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
|
is_nothrow_move_constructible<_Er>,
|
|
is_nothrow_move_assignable<_Tp>,
|
|
is_nothrow_move_assignable<_Er>>)
|
|
requires is_move_assignable_v<_Tp> && is_move_constructible_v<_Tp>
|
|
&& is_move_assignable_v<_Er> && is_move_constructible_v<_Er>
|
|
&& (is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
{
|
|
if (__x._M_has_value)
|
|
_M_assign_val(std::move(__x._M_val));
|
|
else
|
|
_M_assign_unex(std::move(__x._M_unex));
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Up = _Tp>
|
|
requires (!is_same_v<expected, remove_cvref_t<_Up>>)
|
|
&& (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
|
|
&& is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up>
|
|
&& (is_nothrow_constructible_v<_Tp, _Up>
|
|
|| is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
constexpr expected&
|
|
operator=(_Up&& __v)
|
|
{
|
|
_M_assign_val(std::forward<_Up>(__v));
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Gr>
|
|
requires is_constructible_v<_Er, const _Gr&>
|
|
&& is_assignable_v<_Er&, const _Gr&>
|
|
&& (is_nothrow_constructible_v<_Er, const _Gr&>
|
|
|| is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
constexpr expected&
|
|
operator=(const unexpected<_Gr>& __e)
|
|
{
|
|
_M_assign_unex(__e.error());
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Gr>
|
|
requires is_constructible_v<_Er, _Gr>
|
|
&& is_assignable_v<_Er&, _Gr>
|
|
&& (is_nothrow_constructible_v<_Er, _Gr>
|
|
|| is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
constexpr expected&
|
|
operator=(unexpected<_Gr>&& __e)
|
|
{
|
|
_M_assign_unex(std::move(__e).error());
|
|
return *this;
|
|
}
|
|
|
|
// modifiers
|
|
|
|
template<typename... _Args>
|
|
requires is_nothrow_constructible_v<_Tp, _Args...>
|
|
constexpr _Tp&
|
|
emplace(_Args&&... __args) noexcept
|
|
{
|
|
if (_M_has_value)
|
|
std::destroy_at(__builtin_addressof(_M_val));
|
|
else
|
|
{
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
_M_has_value = true;
|
|
}
|
|
std::construct_at(__builtin_addressof(_M_val),
|
|
std::forward<_Args>(__args)...);
|
|
return _M_val;
|
|
}
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_nothrow_constructible_v<_Tp, initializer_list<_Up>&,
|
|
_Args...>
|
|
constexpr _Tp&
|
|
emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept
|
|
{
|
|
if (_M_has_value)
|
|
std::destroy_at(__builtin_addressof(_M_val));
|
|
else
|
|
{
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
_M_has_value = true;
|
|
}
|
|
std::construct_at(__builtin_addressof(_M_val),
|
|
__il, std::forward<_Args>(__args)...);
|
|
return _M_val;
|
|
}
|
|
|
|
// swap
|
|
constexpr void
|
|
swap(expected& __x)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
|
is_nothrow_move_constructible<_Er>,
|
|
is_nothrow_swappable<_Tp&>,
|
|
is_nothrow_swappable<_Er&>>)
|
|
requires is_swappable_v<_Tp> && is_swappable_v<_Er>
|
|
&& is_move_constructible_v<_Tp>
|
|
&& is_move_constructible_v<_Er>
|
|
&& (is_nothrow_move_constructible_v<_Tp>
|
|
|| is_nothrow_move_constructible_v<_Er>)
|
|
{
|
|
if (_M_has_value)
|
|
{
|
|
if (__x._M_has_value)
|
|
{
|
|
using std::swap;
|
|
swap(_M_val, __x._M_val);
|
|
}
|
|
else
|
|
this->_M_swap_val_unex(__x);
|
|
}
|
|
else
|
|
{
|
|
if (__x._M_has_value)
|
|
__x._M_swap_val_unex(*this);
|
|
else
|
|
{
|
|
using std::swap;
|
|
swap(_M_unex, __x._M_unex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// observers
|
|
|
|
[[nodiscard]]
|
|
constexpr const _Tp*
|
|
operator->() const noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return __builtin_addressof(_M_val);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr _Tp*
|
|
operator->() noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return __builtin_addressof(_M_val);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr const _Tp&
|
|
operator*() const & noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return _M_val;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr _Tp&
|
|
operator*() & noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return _M_val;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr const _Tp&&
|
|
operator*() const && noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return std::move(_M_val);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr _Tp&&
|
|
operator*() && noexcept
|
|
{
|
|
__glibcxx_assert(_M_has_value);
|
|
return std::move(_M_val);
|
|
}
|
|
|
|
[[nodiscard]]
|
|
constexpr explicit
|
|
operator bool() const noexcept { return _M_has_value; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool has_value() const noexcept { return _M_has_value; }
|
|
|
|
constexpr const _Tp&
|
|
value() const &
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return _M_val;
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
|
|
}
|
|
|
|
constexpr _Tp&
|
|
value() &
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return _M_val;
|
|
const auto& __unex = _M_unex;
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(__unex));
|
|
}
|
|
|
|
constexpr const _Tp&&
|
|
value() const &&
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return std::move(_M_val);
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(
|
|
std::move(_M_unex)));
|
|
}
|
|
|
|
constexpr _Tp&&
|
|
value() &&
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return std::move(_M_val);
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(
|
|
std::move(_M_unex)));
|
|
}
|
|
|
|
constexpr const _Er&
|
|
error() const & noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return _M_unex;
|
|
}
|
|
|
|
constexpr _Er&
|
|
error() & noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return _M_unex;
|
|
}
|
|
|
|
constexpr const _Er&&
|
|
error() const && noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
constexpr _Er&&
|
|
error() && noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
template<typename _Up>
|
|
constexpr _Tp
|
|
value_or(_Up&& __v) const &
|
|
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
|
|
is_nothrow_convertible<_Up, _Tp>>)
|
|
{
|
|
static_assert( is_copy_constructible_v<_Tp> );
|
|
static_assert( is_convertible_v<_Up, _Tp> );
|
|
|
|
if (_M_has_value)
|
|
return _M_val;
|
|
return static_cast<_Tp>(std::forward<_Up>(__v));
|
|
}
|
|
|
|
template<typename _Up>
|
|
constexpr _Tp
|
|
value_or(_Up&& __v) &&
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
|
is_nothrow_convertible<_Up, _Tp>>)
|
|
{
|
|
static_assert( is_move_constructible_v<_Tp> );
|
|
static_assert( is_convertible_v<_Up, _Tp> );
|
|
|
|
if (_M_has_value)
|
|
return std::move(_M_val);
|
|
return static_cast<_Tp>(std::forward<_Up>(__v));
|
|
}
|
|
|
|
// equality operators
|
|
|
|
template<typename _Up, typename _Er2>
|
|
requires (!is_void_v<_Up>)
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const expected<_Up, _Er2>& __y)
|
|
// FIXME: noexcept(noexcept(bool(*__x == *__y))
|
|
// && noexcept(bool(__x.error() == __y.error())))
|
|
{
|
|
if (__x.has_value())
|
|
return __y.has_value() && bool(*__x == *__y);
|
|
else
|
|
return !__y.has_value() && bool(__x.error() == __y.error());
|
|
}
|
|
|
|
template<typename _Up>
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const _Up& __v)
|
|
// FIXME: noexcept(noexcept(bool(*__x == __v)))
|
|
{ return __x.has_value() && bool(*__x == __v); }
|
|
|
|
template<typename _Er2>
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const unexpected<_Er2>& __e)
|
|
// FIXME: noexcept(noexcept(bool(__x.error() == __e.error())))
|
|
{ return !__x.has_value() && bool(__x.error() == __e.error()); }
|
|
|
|
friend constexpr void
|
|
swap(expected& __x, expected& __y)
|
|
noexcept(noexcept(__x.swap(__y)))
|
|
requires requires {__x.swap(__y);}
|
|
{ __x.swap(__y); }
|
|
|
|
private:
|
|
template<typename, typename> friend class expected;
|
|
|
|
template<typename _Vp>
|
|
constexpr void
|
|
_M_assign_val(_Vp&& __v)
|
|
{
|
|
if (_M_has_value)
|
|
_M_val = std::forward<_Vp>(__v);
|
|
else
|
|
{
|
|
__expected::__reinit(__builtin_addressof(_M_val),
|
|
__builtin_addressof(_M_unex),
|
|
std::forward<_Vp>(__v));
|
|
_M_has_value = true;
|
|
}
|
|
}
|
|
|
|
template<typename _Vp>
|
|
constexpr void
|
|
_M_assign_unex(_Vp&& __v)
|
|
{
|
|
if (_M_has_value)
|
|
{
|
|
__expected::__reinit(__builtin_addressof(_M_unex),
|
|
__builtin_addressof(_M_val),
|
|
std::forward<_Vp>(__v));
|
|
_M_has_value = false;
|
|
}
|
|
else
|
|
_M_unex = std::forward<_Vp>(__v);
|
|
}
|
|
|
|
// Swap two expected objects when only one has a value.
|
|
// Precondition: this->_M_has_value && !__rhs._M_has_value
|
|
constexpr void
|
|
_M_swap_val_unex(expected& __rhs)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Er>,
|
|
is_nothrow_move_constructible<_Tp>>)
|
|
{
|
|
if constexpr (is_nothrow_move_constructible_v<_Er>)
|
|
{
|
|
__expected::_Guard<_Er> __guard(__rhs._M_unex);
|
|
std::construct_at(__builtin_addressof(__rhs._M_val),
|
|
std::move(_M_val)); // might throw
|
|
__rhs._M_has_value = true;
|
|
std::destroy_at(__builtin_addressof(_M_val));
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
__guard.release());
|
|
_M_has_value = false;
|
|
}
|
|
else
|
|
{
|
|
__expected::_Guard<_Tp> __guard(_M_val);
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__rhs._M_unex)); // might throw
|
|
_M_has_value = false;
|
|
std::destroy_at(__builtin_addressof(__rhs._M_unex));
|
|
std::construct_at(__builtin_addressof(__rhs._M_val),
|
|
__guard.release());
|
|
__rhs._M_has_value = true;
|
|
}
|
|
}
|
|
|
|
union {
|
|
_Tp _M_val;
|
|
_Er _M_unex;
|
|
};
|
|
|
|
bool _M_has_value;
|
|
};
|
|
|
|
// Partial specialization for std::expected<cv void, E>
|
|
template<typename _Tp, typename _Er> requires is_void_v<_Tp>
|
|
class expected<_Tp, _Er>
|
|
{
|
|
static_assert( __expected::__can_be_unexpected<_Er> );
|
|
|
|
template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
|
|
static constexpr bool __cons_from_expected
|
|
= __or_v<is_constructible<_Unex, expected<_Up, _Err>&>,
|
|
is_constructible<_Unex, expected<_Up, _Err>>,
|
|
is_constructible<_Unex, const expected<_Up, _Err>&>,
|
|
is_constructible<_Unex, const expected<_Up, _Err>>
|
|
>;
|
|
|
|
public:
|
|
using value_type = _Tp;
|
|
using error_type = _Er;
|
|
using unexpected_type = unexpected<_Er>;
|
|
|
|
template<typename _Up>
|
|
using rebind = expected<_Up, error_type>;
|
|
|
|
constexpr
|
|
expected() noexcept
|
|
: _M_void(), _M_has_value(true)
|
|
{ }
|
|
|
|
expected(const expected&) = default;
|
|
|
|
constexpr
|
|
expected(const expected& __x)
|
|
noexcept(is_nothrow_copy_constructible_v<_Er>)
|
|
requires is_copy_constructible_v<_Er>
|
|
&& (!is_trivially_copy_constructible_v<_Er>)
|
|
: _M_void(), _M_has_value(__x._M_has_value)
|
|
{
|
|
if (!_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
|
|
}
|
|
|
|
expected(expected&&) = default;
|
|
|
|
constexpr
|
|
expected(expected&& __x)
|
|
noexcept(is_nothrow_move_constructible_v<_Er>)
|
|
requires is_move_constructible_v<_Er>
|
|
&& (!is_trivially_move_constructible_v<_Er>)
|
|
: _M_void(), _M_has_value(__x._M_has_value)
|
|
{
|
|
if (!_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x)._M_unex);
|
|
}
|
|
|
|
template<typename _Up, typename _Gr>
|
|
requires is_void_v<_Up>
|
|
&& is_constructible_v<_Er, const _Gr&>
|
|
&& (!__cons_from_expected<_Up, _Gr>)
|
|
constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
|
|
expected(const expected<_Up, _Gr>& __x)
|
|
noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
|
|
: _M_void(), _M_has_value(__x._M_has_value)
|
|
{
|
|
if (!_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
|
|
}
|
|
|
|
template<typename _Up, typename _Gr>
|
|
requires is_void_v<_Up>
|
|
&& is_constructible_v<_Er, _Gr>
|
|
&& (!__cons_from_expected<_Up, _Gr>)
|
|
constexpr explicit(!is_convertible_v<_Gr, _Er>)
|
|
expected(expected<_Up, _Gr>&& __x)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Gr>)
|
|
: _M_void(), _M_has_value(__x._M_has_value)
|
|
{
|
|
if (!_M_has_value)
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x)._M_unex);
|
|
}
|
|
|
|
template<typename _Gr = _Er>
|
|
requires is_constructible_v<_Er, const _Gr&>
|
|
constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
|
|
expected(const unexpected<_Gr>& __u)
|
|
noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
|
|
: _M_unex(__u.error()), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename _Gr = _Er>
|
|
requires is_constructible_v<_Er, _Gr>
|
|
constexpr explicit(!is_convertible_v<_Gr, _Er>)
|
|
expected(unexpected<_Gr>&& __u)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Gr>)
|
|
: _M_unex(std::move(__u).error()), _M_has_value(false)
|
|
{ }
|
|
|
|
constexpr explicit
|
|
expected(in_place_t) noexcept
|
|
: expected()
|
|
{ }
|
|
|
|
template<typename... _Args>
|
|
requires is_constructible_v<_Er, _Args...>
|
|
constexpr explicit
|
|
expected(unexpect_t, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, _Args...>)
|
|
: _M_unex(std::forward<_Args>(__args)...), _M_has_value(false)
|
|
{ }
|
|
|
|
template<typename _Up, typename... _Args>
|
|
requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...>
|
|
constexpr explicit
|
|
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
|
|
noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&,
|
|
_Args...>)
|
|
: _M_unex(__il, std::forward<_Args>(__args)...), _M_has_value(false)
|
|
{ }
|
|
|
|
constexpr ~expected() = default;
|
|
|
|
constexpr ~expected() requires (!is_trivially_destructible_v<_Er>)
|
|
{
|
|
if (!_M_has_value)
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
}
|
|
|
|
// assignment
|
|
|
|
expected& operator=(const expected&) = delete;
|
|
|
|
constexpr expected&
|
|
operator=(const expected& __x)
|
|
noexcept(__and_v<is_nothrow_copy_constructible<_Er>,
|
|
is_nothrow_copy_assignable<_Er>>)
|
|
requires is_copy_constructible_v<_Er>
|
|
&& is_copy_assignable_v<_Er>
|
|
{
|
|
if (__x._M_has_value)
|
|
emplace();
|
|
else
|
|
_M_assign_unex(__x._M_unex);
|
|
return *this;
|
|
}
|
|
|
|
constexpr expected&
|
|
operator=(expected&& __x)
|
|
noexcept(__and_v<is_nothrow_move_constructible<_Er>,
|
|
is_nothrow_move_assignable<_Er>>)
|
|
requires is_move_constructible_v<_Er>
|
|
&& is_move_assignable_v<_Er>
|
|
{
|
|
if (__x._M_has_value)
|
|
emplace();
|
|
else
|
|
_M_assign_unex(std::move(__x._M_unex));
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Gr>
|
|
requires is_constructible_v<_Er, const _Gr&>
|
|
&& is_assignable_v<_Er&, const _Gr&>
|
|
constexpr expected&
|
|
operator=(const unexpected<_Gr>& __e)
|
|
{
|
|
_M_assign_unex(__e.error());
|
|
return *this;
|
|
}
|
|
|
|
template<typename _Gr>
|
|
requires is_constructible_v<_Er, _Gr>
|
|
&& is_assignable_v<_Er&, _Gr>
|
|
constexpr expected&
|
|
operator=(unexpected<_Gr>&& __e)
|
|
{
|
|
_M_assign_unex(std::move(__e.error()));
|
|
return *this;
|
|
}
|
|
|
|
// modifiers
|
|
|
|
constexpr void
|
|
emplace() noexcept
|
|
{
|
|
if (!_M_has_value)
|
|
{
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
_M_has_value = true;
|
|
}
|
|
}
|
|
|
|
// swap
|
|
constexpr void
|
|
swap(expected& __x)
|
|
noexcept(__and_v<is_nothrow_swappable<_Er&>,
|
|
is_nothrow_move_constructible<_Er>>)
|
|
requires is_swappable_v<_Er> && is_move_constructible_v<_Er>
|
|
{
|
|
if (_M_has_value)
|
|
{
|
|
if (!__x._M_has_value)
|
|
{
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::move(__x._M_unex)); // might throw
|
|
std::destroy_at(__builtin_addressof(__x._M_unex));
|
|
_M_has_value = false;
|
|
__x._M_has_value = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (__x._M_has_value)
|
|
{
|
|
std::construct_at(__builtin_addressof(__x._M_unex),
|
|
std::move(_M_unex)); // might throw
|
|
std::destroy_at(__builtin_addressof(_M_unex));
|
|
_M_has_value = true;
|
|
__x._M_has_value = false;
|
|
}
|
|
else
|
|
{
|
|
using std::swap;
|
|
swap(_M_unex, __x._M_unex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// observers
|
|
|
|
[[nodiscard]]
|
|
constexpr explicit
|
|
operator bool() const noexcept { return _M_has_value; }
|
|
|
|
[[nodiscard]]
|
|
constexpr bool has_value() const noexcept { return _M_has_value; }
|
|
|
|
constexpr void
|
|
operator*() const noexcept { __glibcxx_assert(_M_has_value); }
|
|
|
|
constexpr void
|
|
value() const&
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return;
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
|
|
}
|
|
|
|
constexpr void
|
|
value() &&
|
|
{
|
|
if (_M_has_value) [[likely]]
|
|
return;
|
|
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
|
|
}
|
|
|
|
constexpr const _Er&
|
|
error() const & noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return _M_unex;
|
|
}
|
|
|
|
constexpr _Er&
|
|
error() & noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return _M_unex;
|
|
}
|
|
|
|
constexpr const _Er&&
|
|
error() const && noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
constexpr _Er&&
|
|
error() && noexcept
|
|
{
|
|
__glibcxx_assert(!_M_has_value);
|
|
return std::move(_M_unex);
|
|
}
|
|
|
|
// equality operators
|
|
|
|
template<typename _Up, typename _Er2>
|
|
requires is_void_v<_Up>
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const expected<_Up, _Er2>& __y)
|
|
// FIXME: noexcept(noexcept(bool(__x.error() == __y.error())))
|
|
{
|
|
if (__x.has_value())
|
|
return __y.has_value();
|
|
else
|
|
return !__y.has_value() && bool(__x.error() == __y.error());
|
|
}
|
|
|
|
template<typename _Er2>
|
|
friend constexpr bool
|
|
operator==(const expected& __x, const unexpected<_Er2>& __e)
|
|
// FIXME: noexcept(noexcept(bool(__x.error() == __e.error())))
|
|
{ return !__x.has_value() && bool(__x.error() == __e.error()); }
|
|
|
|
friend constexpr void
|
|
swap(expected& __x, expected& __y)
|
|
noexcept(noexcept(__x.swap(__y)))
|
|
requires requires { __x.swap(__y); }
|
|
{ __x.swap(__y); }
|
|
|
|
private:
|
|
template<typename, typename> friend class expected;
|
|
|
|
template<typename _Vp>
|
|
constexpr void
|
|
_M_assign_unex(_Vp&& __v)
|
|
{
|
|
if (_M_has_value)
|
|
{
|
|
std::construct_at(__builtin_addressof(_M_unex),
|
|
std::forward<_Vp>(__v));
|
|
_M_has_value = false;
|
|
}
|
|
else
|
|
_M_unex = std::forward<_Vp>(__v);
|
|
}
|
|
|
|
|
|
union {
|
|
struct { } _M_void;
|
|
_Er _M_unex;
|
|
};
|
|
|
|
bool _M_has_value;
|
|
};
|
|
/// @}
|
|
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace std
|
|
|
|
#endif // C++23
|
|
#endif // _GLIBCXX_EXPECTED
|