This implements proposed resolution for LWG4308 [1]. For T denoting either function type or unbounded array, the optional<T&> no longer exposes iterator, and viable begin/end members. The conditionally provided iterator type, it is now defined in __optional_ref_base base class.
Furthermore, range support for optional<T&> is now also guarded by __cpp_lib_optional_range_support. [1] https://cplusplus.github.io/LWG/issue4308 PR libstdc++/122396 libstdc++-v3/ChangeLog: * include/std/optional (__optional_ref_base): Define. (std::optional<_Tp&>): Inherit from __optional_ref_base<_Tp>. (optional<_Tp&>::iterator): Move to base class. (optional<_Tp&>::begin, optional<_Tp&>::end): Use deduced return type and constrain accordingly. * testsuite/20_util/optional/range.cc: Add test for optional<T&>. --- v2 guards range support for optional<T&> with __cpp_lib_optional_range_support. libstdc++-v3/include/std/optional | 39 +++++++--- .../testsuite/20_util/optional/range.cc | 56 +++++++++++---- .../testsuite/29_atomics/atomic_ref/bool.cc | 18 ++++- .../29_atomics/atomic_ref/requirements.cc | 71 +++++++++++-------- 4 files changed, 131 insertions(+), 53 deletions(-) diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index c4b56e31d58..d191e51ed79 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -1484,13 +1484,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cpp_lib_optional >= 202506L // C++26 template<typename _Tp> - class optional<_Tp&> + class optional<_Tp&>; + + template<typename _Tp> + struct __optional_ref_base + {}; + +#ifdef __cpp_lib_optional_range_support // >= C++26 + template<typename _Tp> + struct __optional_ref_base<_Tp[]> + {}; + + template<typename _Tp> + requires is_object_v<_Tp> + struct __optional_ref_base<_Tp> + { + using iterator = __gnu_cxx::__normal_iterator<_Tp*, optional<_Tp&>>; + }; +#endif // __cpp_lib_optional_range_support + + template<typename _Tp> + class optional<_Tp&> : public __optional_ref_base<_Tp> { static_assert(__is_valid_contained_type_for_optional<_Tp&>); public: using value_type = _Tp; - using iterator = __gnu_cxx::__normal_iterator<_Tp*, optional>; // Constructors. constexpr optional() noexcept = default; @@ -1652,16 +1671,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr void swap(optional& __rhs) noexcept { std::swap(_M_val, __rhs._M_val); } +#ifdef __cpp_lib_optional_range_support // >= C++26 // Iterator support. - constexpr iterator begin() const noexcept - { - return iterator(_M_val); - } + constexpr auto begin() const noexcept + requires is_object_v<_Tp> && (!is_unbounded_array_v<_Tp>) + { return __gnu_cxx::__normal_iterator<_Tp*, optional>(_M_val); } - constexpr iterator end() const noexcept - { - return begin() + has_value(); - } + constexpr auto end() const noexcept + requires is_object_v<_Tp> && (!is_unbounded_array_v<_Tp>) + { return begin() + has_value(); } +#endif // __cpp_lib_optional_range_support // Observers. constexpr _Tp* operator->() const noexcept diff --git a/libstdc++-v3/testsuite/20_util/optional/range.cc b/libstdc++-v3/testsuite/20_util/optional/range.cc index e77dc21e22b..1cb3eb6ceff 100644 --- a/libstdc++-v3/testsuite/20_util/optional/range.cc +++ b/libstdc++-v3/testsuite/20_util/optional/range.cc @@ -10,18 +10,18 @@ #include <testsuite_hooks.h> -template<typename O> +template<typename T> constexpr void test_range_concepts() { + using O = std::optional<T>; static_assert(std::ranges::contiguous_range<O>); static_assert(std::ranges::sized_range<O>); static_assert(std::ranges::common_range<O>); static_assert(!std::ranges::borrowed_range<O>); // an optional<const T> is not assignable, and therefore does not satisfy ranges::view - using T = typename O::value_type; constexpr bool is_const_opt = std::is_const_v<T>; static_assert(std::ranges::view<O> == !is_const_opt); static_assert(std::ranges::viewable_range<O> == !is_const_opt); @@ -39,7 +39,14 @@ test_iterator_concepts() static_assert(std::is_same_v<std::iter_value_t<iterator>, std::remove_cv_t<T>>); static_assert(std::is_same_v<typename std::iterator_traits<iterator>::reference, T&>); static_assert(std::is_same_v<std::iter_reference_t<iterator>, T&>); +} +template<typename O> +constexpr +void +test_const_iterator_concepts() +{ + using T = typename O::value_type; using const_iterator = typename O::const_iterator; static_assert(std::contiguous_iterator<const_iterator>); static_assert(std::is_same_v<typename std::iterator_traits<const_iterator>::value_type, std::remove_cv_t<T>>); @@ -48,11 +55,12 @@ test_iterator_concepts() static_assert(std::is_same_v<std::iter_reference_t<const_iterator>, const T&>); } -template<typename O> +template<typename T> constexpr void test_empty() { + using O = std::optional<T>; O empty; VERIFY(!empty); VERIFY(empty.begin() == empty.end()); @@ -69,14 +77,18 @@ test_empty() VERIFY(count == 0); } -template<typename O, typename T> +template<typename T> constexpr void test_non_empty(const T& value) { - O non_empty = std::make_optional(value); + using O = std::optional<T>; + using V = typename O::value_type; + O non_empty(std::in_place, value); VERIFY(non_empty); - VERIFY(*non_empty == value); + if constexpr (!std::is_array_v<V>) + VERIFY(*non_empty == value); + VERIFY(non_empty.begin() != non_empty.end()); VERIFY(non_empty.begin() < non_empty.end()); VERIFY(std::as_const(non_empty).begin() != std::as_const(non_empty).end()); @@ -92,11 +104,11 @@ test_non_empty(const T& value) ++count; VERIFY(count == 1); - if constexpr (!std::is_const_v<typename O::value_type>) { + if constexpr (!std::is_const_v<V> && !std::is_array_v<V>) { for (auto& x : non_empty) - x = T{}; + x = V{}; VERIFY(non_empty); - VERIFY(*non_empty == T{}); + VERIFY(*non_empty == V{}); } } @@ -106,10 +118,12 @@ void test(const T& value) { using O = std::optional<T>; - test_range_concepts<O>(); + test_range_concepts<T>(); test_iterator_concepts<O>(); - test_empty<O>(); - test_non_empty<O>(value); + if constexpr (!std::is_reference_v<T>) + test_const_iterator_concepts<O>(); + test_empty<T>(); + test_non_empty<T>(value); static_assert(!std::formattable<O, char>); static_assert(!std::formattable<O, wchar_t>); static_assert(std::format_kind<O> == std::range_format::disabled); @@ -142,18 +156,36 @@ range_chain_example() // from P3168 VERIFY(ok); } +template<typename T> +constexpr void test_not_range() +{ + static_assert(!requires { typename std::optional<T>::iterator; }); + static_assert(!requires(std::optional<T> o) { o.begin(); }); + static_assert(!requires(std::optional<T> o) { o.end(); }); +}; + constexpr bool all_tests() { test(42); int i = 42; + int arr[10]{}; test(&i); test(std::string_view("test")); test(std::vector<int>{1, 2, 3, 4}); test(std::optional<int>(42)); test<const int>(42); + test<int&>(i); + test<const int&>(i); + test<int(&)[10]>(arr); + test<const int(&)[10]>(arr); + test_not_range<void(&)()>(); + test_not_range<void(&)(int)>(); + test_not_range<int(&)[]>(); + test_not_range<const int(&)[]>(); + range_chain_example(); return true; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc index c73319010ee..61256a76117 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc @@ -66,8 +66,6 @@ test02() std::atomic_ref<bool> a0(b); std::atomic_ref<bool> a1(b); std::atomic_ref<const bool> a1c(b); - std::atomic_ref<volatile bool> a1v(b); - std::atomic_ref<const volatile bool> a1cv(b); std::atomic_ref<bool> a2(a0); b = true; VERIFY( a1.load() ); @@ -77,9 +75,25 @@ test02() VERIFY( a2.load() ); } +template<typename T = bool> +void +test03() +{ + if constexpr (std::atomic_ref<T>::is_always_lock_free) + { + bool b = false; + std::atomic_ref<volatile T> a1v(b); + std::atomic_ref<const volatile T> a1cv(b); + b = true; + VERIFY( a1v.load() ); + VERIFY( a1cv.load() ); + } +} + int main() { test01(); test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc index 8617661f8e1..930138edb22 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc @@ -21,17 +21,21 @@ #include <type_traits> template <class T> + requires (!std::is_volatile_v<T>) || std::atomic_ref<std::remove_cv_t<T>>::is_always_lock_free void test_generic() { using A = std::atomic_ref<T>; - static_assert( std::is_standard_layout_v<A> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( std::is_trivially_destructible_v<A> ); - static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); - static_assert( !requires { typename A::difference_type; } ); - static_assert( !std::is_copy_assignable_v<A> ); - static_assert( !std::is_move_assignable_v<A> ); + if constexpr (!std::is_volatile_v<T> || A::is_always_lock_free) + { + static_assert( std::is_standard_layout_v<A> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( std::is_trivially_destructible_v<A> ); + static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); + static_assert( !requires { typename A::difference_type; } ); + static_assert( !std::is_copy_assignable_v<A> ); + static_assert( !std::is_move_assignable_v<A> ); + } } template <class T> @@ -39,13 +43,16 @@ void test_integral() { using A = std::atomic_ref<T>; - static_assert( std::is_standard_layout_v<A> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( std::is_trivially_destructible_v<A> ); - static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); - static_assert( std::is_same_v<typename A::difference_type, typename A::value_type> ); - static_assert( !std::is_copy_assignable_v<A> ); - static_assert( !std::is_move_assignable_v<A> ); + if constexpr (!std::is_volatile_v<T> || A::is_always_lock_free) + { + static_assert( std::is_standard_layout_v<A> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( std::is_trivially_destructible_v<A> ); + static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); + static_assert( std::is_same_v<typename A::difference_type, typename A::value_type> ); + static_assert( !std::is_copy_assignable_v<A> ); + static_assert( !std::is_move_assignable_v<A> ); + } } template <class T> @@ -53,13 +60,16 @@ void test_floating_point() { using A = std::atomic_ref<T>; - static_assert( std::is_standard_layout_v<A> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( std::is_trivially_destructible_v<A> ); - static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); - static_assert( std::is_same_v<typename A::difference_type, typename A::value_type> ); - static_assert( !std::is_copy_assignable_v<A> ); - static_assert( !std::is_move_assignable_v<A> ); + if constexpr (!std::is_volatile_v<T> || A::is_always_lock_free) + { + static_assert( std::is_standard_layout_v<A> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( std::is_trivially_destructible_v<A> ); + static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); + static_assert( std::is_same_v<typename A::difference_type, typename A::value_type> ); + static_assert( !std::is_copy_assignable_v<A> ); + static_assert( !std::is_move_assignable_v<A> ); + } } template <class T> @@ -67,14 +77,17 @@ void test_pointer() { using A = std::atomic_ref<T>; - static_assert( std::is_standard_layout_v<A> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( std::is_trivially_destructible_v<A> ); - static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); - static_assert( std::is_same_v<typename A::difference_type, std::ptrdiff_t> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( !std::is_copy_assignable_v<A> ); - static_assert( !std::is_move_assignable_v<A> ); + if constexpr (!std::is_volatile_v<T> || A::is_always_lock_free) + { + static_assert( std::is_standard_layout_v<A> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( std::is_trivially_destructible_v<A> ); + static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); + static_assert( std::is_same_v<typename A::difference_type, std::ptrdiff_t> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( !std::is_copy_assignable_v<A> ); + static_assert( !std::is_move_assignable_v<A> ); + } } int -- 2.51.0
