@@ -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) &&
{
new file mode 100644
@@ -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 );
+}
new file mode 100644
@@ -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 );
+}