diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2024-07-25 23:08:14 +0100 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2024-07-30 21:14:28 +0100 |
commit | a9e472c6b748abde55b5ecde2e2d98dcb2f96ded (patch) | |
tree | 97467bf6b836645ef6eb837b8c4abd560f177524 | |
parent | acc70606c59e3f14072cc8a164362e728d8df5d6 (diff) | |
download | gcc-a9e472c6b748abde55b5ecde2e2d98dcb2f96ded.zip gcc-a9e472c6b748abde55b5ecde2e2d98dcb2f96ded.tar.gz gcc-a9e472c6b748abde55b5ecde2e2d98dcb2f96ded.tar.bz2 |
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.
-rw-r--r-- | libstdc++-v3/include/std/expected | 8 | ||||
-rw-r--r-- | libstdc++-v3/include/std/optional | 12 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/expected/lwg3886.cc | 58 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/20_util/optional/cons/lwg3886.cc | 58 |
4 files changed, 126 insertions, 10 deletions
diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected index 515a1e6..b8217e5 100644 --- a/libstdc++-v3/include/std/expected +++ b/libstdc++-v3/include/std/expected @@ -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>, diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index 4694d59..2c4cc26 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -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) && { diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg3886.cc b/libstdc++-v3/testsuite/20_util/expected/lwg3886.cc new file mode 100644 index 0000000..cf1a2ce --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/expected/lwg3886.cc @@ -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 ); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/cons/lwg3886.cc b/libstdc++-v3/testsuite/20_util/optional/cons/lwg3886.cc new file mode 100644 index 0000000..f6323ca --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/cons/lwg3886.cc @@ -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 ); +} |