From 1374f01655c922abe077fadf1938d05ad9b8961a Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Tue, 23 Jun 2026 19:02:48 +0800 Subject: [PATCH 1/2] Implement LWG-4218 --- stl/inc/xutility | 16 ++++++------ .../P2278R4_basic_const_iterator/test.cpp | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 488bc52d044..9fe7f1db1ec 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2605,30 +2605,30 @@ public: return _Current <=> _Right; } - template <_Not_a_const_iterator _Other> + template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter> requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> - _NODISCARD friend constexpr bool operator<(const _Other& _Left, const basic_const_iterator& _Right) + _NODISCARD friend constexpr bool operator<(const _Other& _Left, const basic_const_iterator<_Jter>& _Right) noexcept(noexcept(_STD _Fake_copy_init(_Left < _Right._Current))) /* strengthened */ { return _Left < _Right._Current; } - template <_Not_a_const_iterator _Other> + template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter> requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> - _NODISCARD friend constexpr bool operator>(const _Other& _Left, const basic_const_iterator& _Right) + _NODISCARD friend constexpr bool operator>(const _Other& _Left, const basic_const_iterator<_Jter>& _Right) noexcept(noexcept(_STD _Fake_copy_init(_Left > _Right._Current))) /* strengthened */ { return _Left > _Right._Current; } - template <_Not_a_const_iterator _Other> + template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter> requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> - _NODISCARD friend constexpr bool operator<=(const _Other& _Left, const basic_const_iterator& _Right) + _NODISCARD friend constexpr bool operator<=(const _Other& _Left, const basic_const_iterator<_Jter>& _Right) noexcept(noexcept(_STD _Fake_copy_init(_Left <= _Right._Current))) /* strengthened */ { return _Left <= _Right._Current; } - template <_Not_a_const_iterator _Other> + template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter> requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> - _NODISCARD friend constexpr bool operator>=(const _Other& _Left, const basic_const_iterator& _Right) + _NODISCARD friend constexpr bool operator>=(const _Other& _Left, const basic_const_iterator<_Jter>& _Right) noexcept(noexcept(_STD _Fake_copy_init(_Left >= _Right._Current))) /* strengthened */ { return _Left >= _Right._Current; } diff --git a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp index 47ebdfdc390..5fe2d3e37c6 100644 --- a/tests/std/tests/P2278R4_basic_const_iterator/test.cpp +++ b/tests/std/tests/P2278R4_basic_const_iterator/test.cpp @@ -270,6 +270,32 @@ static constexpr int some_ints[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; using Zipped = decltype(views::zip(some_ints) | views::as_const | views::as_rvalue); static_assert(same_as, tuple>); +// LWG-4218 "Constraint recursion in basic_const_iterator's relational operators due to ADL + CWG 2369" +namespace lwg_4218 { + template + struct adl_conversion_source { + operator T() const; + + friend bool operator==(const adl_conversion_source&, const adl_conversion_source&) { + return true; + } + friend bool operator==(const adl_conversion_source&, const T&) { + return true; + } + + template Self> + friend partial_ordering operator<=>(const adl_conversion_source&, const Self&) { + return partial_ordering::equivalent; + } + }; + + using cvi = basic_const_iterator::iterator>; + static_assert(totally_ordered_with, cvi>); + + using rcvi = reverse_iterator; + static_assert(totally_ordered); +} // namespace lwg_4218 + struct instantiator { template static constexpr void call() { From 9cfc3093fae6d5d3e35d1580ecf236d6bedb1023 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Tue, 23 Jun 2026 20:11:41 +0800 Subject: [PATCH 2/2] Workaround for DevCom-11111696 --- stl/inc/xutility | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 9fe7f1db1ec..4a7f38b3d85 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2605,28 +2605,48 @@ public: return _Current <=> _Right; } - template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter> + template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-11111696 + , + _Iter* = nullptr +#endif // ^^^ workaround ^^^ + > requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> _NODISCARD friend constexpr bool operator<(const _Other& _Left, const basic_const_iterator<_Jter>& _Right) noexcept(noexcept(_STD _Fake_copy_init(_Left < _Right._Current))) /* strengthened */ { return _Left < _Right._Current; } - template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter> + template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-11111696 + , + _Iter* = nullptr +#endif // ^^^ workaround ^^^ + > requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> _NODISCARD friend constexpr bool operator>(const _Other& _Left, const basic_const_iterator<_Jter>& _Right) noexcept(noexcept(_STD _Fake_copy_init(_Left > _Right._Current))) /* strengthened */ { return _Left > _Right._Current; } - template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter> + template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-11111696 + , + _Iter* = nullptr +#endif // ^^^ workaround ^^^ + > requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> _NODISCARD friend constexpr bool operator<=(const _Other& _Left, const basic_const_iterator<_Jter>& _Right) noexcept(noexcept(_STD _Fake_copy_init(_Left <= _Right._Current))) /* strengthened */ { return _Left <= _Right._Current; } - template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter> + template <_Not_a_const_iterator _Other, same_as<_Iter> _Jter +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-11111696 + , + _Iter* = nullptr +#endif // ^^^ workaround ^^^ + > requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Other> _NODISCARD friend constexpr bool operator>=(const _Other& _Left, const basic_const_iterator<_Jter>& _Right) noexcept(noexcept(_STD _Fake_copy_init(_Left >= _Right._Current))) /* strengthened */ {