From: Tomasz Kamiński Date: Thu, 6 Nov 2025 14:22:12 +0000 (+0100) Subject: libstdc++: optional for function and unbounded array should not be range [PR122396] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d554b8a704bae6c3470fbf44a95559fc26bc75d1;p=thirdparty%2Fgcc.git libstdc++: optional for function and unbounded array should not be range [PR122396] This implements proposed resolution for LWG4308 [1]. For T denoting either function type or unbounded array, the optional 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 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. Reviewed-by: Jonathan Wakely Signed-off-by: Tomasz Kamiński --- 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 - class optional<_Tp&> + class optional<_Tp&>; + + template + struct __optional_ref_base + {}; + +#ifdef __cpp_lib_optional_range_support // >= C++26 + template + struct __optional_ref_base<_Tp[]> + {}; + + template + 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 + 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 -template +template constexpr void test_range_concepts() { + using O = std::optional; static_assert(std::ranges::contiguous_range); static_assert(std::ranges::sized_range); static_assert(std::ranges::common_range); static_assert(!std::ranges::borrowed_range); // an optional 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; static_assert(std::ranges::view == !is_const_opt); static_assert(std::ranges::viewable_range == !is_const_opt); @@ -39,7 +39,14 @@ test_iterator_concepts() static_assert(std::is_same_v, std::remove_cv_t>); static_assert(std::is_same_v::reference, T&>); static_assert(std::is_same_v, T&>); +} +template +constexpr +void +test_const_iterator_concepts() +{ + using T = typename O::value_type; using const_iterator = typename O::const_iterator; static_assert(std::contiguous_iterator); static_assert(std::is_same_v::value_type, std::remove_cv_t>); @@ -48,11 +55,12 @@ test_iterator_concepts() static_assert(std::is_same_v, const T&>); } -template +template constexpr void test_empty() { + using O = std::optional; O empty; VERIFY(!empty); VERIFY(empty.begin() == empty.end()); @@ -69,14 +77,18 @@ test_empty() VERIFY(count == 0); } -template +template constexpr void test_non_empty(const T& value) { - O non_empty = std::make_optional(value); + using O = std::optional; + 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) + 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) { + if constexpr (!std::is_const_v && !std::is_array_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; - test_range_concepts(); + test_range_concepts(); test_iterator_concepts(); - test_empty(); - test_non_empty(value); + if constexpr (!std::is_reference_v) + test_const_iterator_concepts(); + test_empty(); + test_non_empty(value); static_assert(!std::formattable); static_assert(!std::formattable); static_assert(std::format_kind == std::range_format::disabled); @@ -142,18 +156,36 @@ range_chain_example() // from P3168 VERIFY(ok); } +template +constexpr void test_not_range() +{ + static_assert(!requires { typename std::optional::iterator; }); + static_assert(!requires(std::optional o) { o.begin(); }); + static_assert(!requires(std::optional 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{1, 2, 3, 4}); test(std::optional(42)); test(42); + test(i); + test(i); + test(arr); + test(arr); + test_not_range(); + test_not_range(); + test_not_range(); + test_not_range(); + range_chain_example(); return true;