libstdc++: Implement LWG 3886 for std::optional and std::expected
This uses remove_cv_t<T> for the default template argument used for deducing a type for a braced-init-list used with std::optional and std::expected. libstdc++-v3/ChangeLog: * include/std/expected (expected(U&&), operator=(U&&)) (value_or): Use remove_cv_t on default template argument, as per LWG 3886. * include/std/optional (optional(U&&), operator=(U&&)) (value_or): Likewise. * testsuite/20_util/expected/lwg3886.cc: New test. * testsuite/20_util/optional/cons/lwg3886.cc: New test.
This commit is contained in:
parent
acc70606c5
commit
a9e472c6b7
4 changed files with 126 additions and 10 deletions
|
@ -468,7 +468,7 @@ namespace __expected
|
|||
std::move(__x)._M_unex);
|
||||
}
|
||||
|
||||
template<typename _Up = _Tp>
|
||||
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_constructible_v<_Tp, _Up>
|
||||
|
@ -582,7 +582,7 @@ namespace __expected
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename _Up = _Tp>
|
||||
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>
|
||||
|
@ -818,7 +818,7 @@ namespace __expected
|
|||
return std::move(_M_unex);
|
||||
}
|
||||
|
||||
template<typename _Up>
|
||||
template<typename _Up = remove_cv_t<_Tp>>
|
||||
constexpr _Tp
|
||||
value_or(_Up&& __v) const &
|
||||
noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
|
||||
|
@ -832,7 +832,7 @@ namespace __expected
|
|||
return static_cast<_Tp>(std::forward<_Up>(__v));
|
||||
}
|
||||
|
||||
template<typename _Up>
|
||||
template<typename _Up = remove_cv_t<_Tp>>
|
||||
constexpr _Tp
|
||||
value_or(_Up&& __v) &&
|
||||
noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
|
||||
|
|
|
@ -868,7 +868,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
|
||||
// Converting constructors for engaged optionals.
|
||||
#ifdef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL
|
||||
template<typename _Up = _Tp>
|
||||
template<typename _Up = remove_cv_t<_Tp>>
|
||||
requires (!is_same_v<optional, remove_cvref_t<_Up>>)
|
||||
&& (!is_same_v<in_place_t, remove_cvref_t<_Up>>)
|
||||
&& is_constructible_v<_Tp, _Up>
|
||||
|
@ -919,7 +919,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
: _Base(std::in_place, __il, std::forward<_Args>(__args)...)
|
||||
{ }
|
||||
#else
|
||||
template<typename _Up = _Tp,
|
||||
template<typename _Up = remove_cv_t<_Tp>,
|
||||
_Requires<__not_self<_Up>, __not_tag<_Up>,
|
||||
is_constructible<_Tp, _Up>,
|
||||
is_convertible<_Up, _Tp>,
|
||||
|
@ -929,7 +929,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
|
||||
: _Base(std::in_place, std::forward<_Up>(__t)) { }
|
||||
|
||||
template<typename _Up = _Tp,
|
||||
template<typename _Up = remove_cv_t<_Tp>,
|
||||
_Requires<__not_self<_Up>, __not_tag<_Up>,
|
||||
is_constructible<_Tp, _Up>,
|
||||
__not_<is_convertible<_Up, _Tp>>,
|
||||
|
@ -1017,7 +1017,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
return *this;
|
||||
}
|
||||
|
||||
template<typename _Up = _Tp>
|
||||
template<typename _Up = remove_cv_t<_Tp>>
|
||||
#ifdef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL
|
||||
requires (!is_same_v<optional, remove_cvref_t<_Up>>)
|
||||
&& (!(is_scalar_v<_Tp> && is_same_v<_Tp, decay_t<_Up>>))
|
||||
|
@ -1242,7 +1242,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
__throw_bad_optional_access();
|
||||
}
|
||||
|
||||
template<typename _Up>
|
||||
template<typename _Up = remove_cv_t<_Tp>>
|
||||
constexpr _Tp
|
||||
value_or(_Up&& __u) const&
|
||||
{
|
||||
|
@ -1255,7 +1255,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
return static_cast<_Tp>(std::forward<_Up>(__u));
|
||||
}
|
||||
|
||||
template<typename _Up>
|
||||
template<typename _Up = remove_cv_t<_Tp>>
|
||||
constexpr _Tp
|
||||
value_or(_Up&& __u) &&
|
||||
{
|
||||
|
|
58
libstdc++-v3/testsuite/20_util/expected/lwg3886.cc
Normal file
58
libstdc++-v3/testsuite/20_util/expected/lwg3886.cc
Normal file
|
@ -0,0 +1,58 @@
|
|||
// { dg-do compile { target c++23 } }
|
||||
|
||||
// LWG 3886. Monad mo' problems
|
||||
|
||||
#include <expected>
|
||||
|
||||
void
|
||||
test_constructor()
|
||||
{
|
||||
struct MoveOnly {
|
||||
MoveOnly(int, int) { }
|
||||
MoveOnly(MoveOnly&&) { }
|
||||
};
|
||||
|
||||
// The {0,0} should be deduced as MoveOnly not const MoveOnly
|
||||
[[maybe_unused]] std::expected<const MoveOnly, int> e({0,0});
|
||||
}
|
||||
|
||||
struct Tracker {
|
||||
bool moved = false;
|
||||
constexpr Tracker(int, int) { }
|
||||
constexpr Tracker(const Tracker&) { }
|
||||
constexpr Tracker(Tracker&&) : moved(true) { }
|
||||
|
||||
// The follow means that is_assignable<const Tracker&, U> is true:
|
||||
template<typename T> constexpr void operator=(T&&) const { }
|
||||
// This stops a copy assignment from being declared implicitly:
|
||||
void operator=(Tracker&) = delete;
|
||||
};
|
||||
|
||||
void
|
||||
test_assignment()
|
||||
{
|
||||
constexpr bool moved = [] {
|
||||
std::expected<const Tracker, int> e(std::unexpect);
|
||||
// The {0,0} should be deduced as Tracker not const Tracker:
|
||||
e = {0,0};
|
||||
// So the contained value should have been move constructed not copied:
|
||||
return e->moved;
|
||||
}();
|
||||
static_assert( moved );
|
||||
}
|
||||
|
||||
void
|
||||
test_value_or()
|
||||
{
|
||||
constexpr bool moved = [] {
|
||||
const std::expected<const Tracker, int> e(std::unexpect, 1);
|
||||
return e.value_or({0,0}).moved;
|
||||
}();
|
||||
static_assert( moved );
|
||||
|
||||
constexpr bool moved_rval = [] {
|
||||
std::expected<const Tracker, int> e(std::unexpect, 1);
|
||||
return std::move(e).value_or({0,0}).moved;
|
||||
}();
|
||||
static_assert( moved_rval );
|
||||
}
|
58
libstdc++-v3/testsuite/20_util/optional/cons/lwg3886.cc
Normal file
58
libstdc++-v3/testsuite/20_util/optional/cons/lwg3886.cc
Normal file
|
@ -0,0 +1,58 @@
|
|||
// { dg-do compile { target c++17 } }
|
||||
|
||||
// LWG 3886. Monad mo' problems
|
||||
|
||||
#include <optional>
|
||||
|
||||
void
|
||||
test_cons()
|
||||
{
|
||||
struct MoveOnly {
|
||||
MoveOnly(int, int) { }
|
||||
MoveOnly(MoveOnly&&) { }
|
||||
};
|
||||
|
||||
// The {0,0} should be deduced as MoveOnly not const MoveOnly
|
||||
[[maybe_unused]] std::optional<const MoveOnly> o({0,0});
|
||||
}
|
||||
|
||||
struct Tracker {
|
||||
bool moved = false;
|
||||
constexpr Tracker(int, int) { }
|
||||
constexpr Tracker(const Tracker&) { }
|
||||
constexpr Tracker(Tracker&&) : moved(true) { }
|
||||
|
||||
// The follow means that is_assignable<const Tracker&, U> is true:
|
||||
template<typename T> constexpr void operator=(T&&) const { }
|
||||
};
|
||||
|
||||
#if __cpp_lib_optional >= 202106L // for constexpr assignment
|
||||
void
|
||||
test_assignment()
|
||||
{
|
||||
constexpr bool moved = [] {
|
||||
std::optional<const Tracker> o;
|
||||
// The {0,0} should be deduced as Tracker not const Tracker:
|
||||
o = {0,0};
|
||||
// So the contained value should have been move constructed not copied:
|
||||
return o->moved;
|
||||
}();
|
||||
static_assert( moved );
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
test_value_or()
|
||||
{
|
||||
constexpr bool moved = [] {
|
||||
std::optional<const Tracker> o;
|
||||
return o.value_or({0,0}).moved;
|
||||
}();
|
||||
static_assert( moved );
|
||||
|
||||
constexpr bool moved_rval = [] {
|
||||
std::optional<const Tracker> o;
|
||||
return std::move(o).value_or({0,0}).moved;
|
||||
}();
|
||||
static_assert( moved_rval );
|
||||
}
|
Loading…
Add table
Reference in a new issue