<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Thu, Jun 25, 2026 at 4:15 PM Jonathan Wakely <<a href="mailto:jwakely@redhat.com">jwakely@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Thu, 25 Jun 2026 at 14:02, Tomasz Kaminski <<a href="mailto:tkaminsk@redhat.com" target="_blank">tkaminsk@redhat.com</a>> wrote:<br>
><br>
><br>
><br>
><br>
> On Thu, Jun 25, 2026 at 2:51 PM Jonathan Wakely <<a href="mailto:jwakely@redhat.com" target="_blank">jwakely@redhat.com</a>> wrote:<br>
>><br>
>> On Thu, 25 Jun 2026 at 13:31, Tomasz Kamiński <<a href="mailto:tkaminsk@redhat.com" target="_blank">tkaminsk@redhat.com</a>> wrote:<br>
>> ><br>
>> > This implements remaining changes from P3612R1 Harmonize proxy-reference<br>
>> > operations (LWG 3638 and 4187).<br>
>> ><br>
>> > As the const-assigment operator in bitset::reference (from LWG4187)<br>
>> > is used by range concepts (std::indirectly_writable) to recognize<br>
>> > iterator returning it by value as proxy iterator (see range_proxy.cc) test,<br>
>> > this change is backported to C++23. This puts it inline with vector<bool><br>
>> > reference and other proxy type, for which the coresponding assigment<br>
>> > operator where added by P2321 in C++20 and implemented in r13-2159-g72886f.<br>
>> ><br>
>> > Similary, a swap overloads accepting bitset::reference by value and bool&,<br>
>> > are necessary for ranges::swap and ranges::iter_swap to work correctly,<br>
>> > so they should also backported. This patch follows the approach from<br>
>> > resolution of LWG3638 from r12-5311-g59434931fb658f, and backports the<br>
>> > overloads to C++11. This harmonizes the operations on this two proxy<br>
>> > types (per paper title). Corresponding test are added to both classes.<br>
>> ><br>
>> > The deprecation warning for static vector<bool>::swap is added sinc C++26.<br>
>> > The text in _GLIBCXX26_DEPRECATED_SUGGEST leads to following message:<br>
>> > 'static constexpr void std::vector<bool, _Alloc>::swap(reference, reference)<br>
>> > [with... ]' is deprecated: use 'swap' invoked via ADL or 'std::ranges::swap'<br>
>> > instead<br>
>> ><br>
>> > libstdc++-v3/ChangeLog:<br>
>> ><br>
>> > * include/bits/stl_bvector.h: Add _GLIBCXX_RESOLVE_LIB_DEFECTS<br>
>> > comment for 3638.<br>
>> > (vector<bool, _Alloc>::swap): Add deprected attribute via<br>
>> > _GLIBCXX26_DEPRECATED_SUGGEST.<br>
>> > * include/std/bitset (bitset::operator=(bool) const)<br>
>> > [__cplusplus > 202002]: Define.<br>
>> > (bistset::reference::swap) [__cplusplus >= 201103L]: Define.<br>
>> > * testsuite/20_util/bitset/access/range_proxy.cc: New test.<br>
>> > * testsuite/20_util/bitset/access/ref_swap.cc: New test.<br>
>> > * testsuite/23_containers/vector/bool/element_access/range_proxy.cc:<br>
>> > New test.<br>
>> > * testsuite/23_containers/vector/bool/element_access/ref_swap.cc:<br>
>> > New test.<br>
>> > * testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc:<br>
>> > Checkfor deprecated warning on static swap.<br>
>> > ---<br>
>> > Do you agree with my backport choice for swap? I think the minimum is<br>
>> > since C++23.<br>
>><br>
>> I don't remember why I chose C++11 for the swap overloads for<br>
>> vector<bool>::reference, but being consistent with that makes sense.<br>
>><br>
>> ><br>
>> > Testing on x86_64-linux. *bitset* and *vector/bool* test already passed<br>
>> > in all standard modes. OK for trunk?<br>
>> ><br>
>> > libstdc++-v3/include/bits/stl_bvector.h | 4 +<br>
>> > libstdc++-v3/include/std/bitset | 43 +++++++++<br>
>> > .../20_util/bitset/access/range_proxy.cc | 94 +++++++++++++++++++<br>
>> > .../20_util/bitset/access/ref_swap.cc | 28 ++++++<br>
>> > .../vector/bool/element_access/range_proxy.cc | 62 ++++++++++++<br>
>> > .../vector/bool/element_access/ref_swap.cc | 38 ++++++++<br>
>> > .../vector/bool/modifiers/swap/constexpr.cc | 2 +-<br>
>> > 7 files changed, 270 insertions(+), 1 deletion(-)<br>
>> > create mode 100644 libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc<br>
>> > create mode 100644 libstdc++-v3/testsuite/20_util/bitset/access/ref_swap.cc<br>
>> > create mode 100644 libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc<br>
>> > create mode 100644 libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc<br>
>> ><br>
>> > diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h<br>
>> > index f190d1be5ce..0637e687cbb 100644<br>
>> > --- a/libstdc++-v3/include/bits/stl_bvector.h<br>
>> > +++ b/libstdc++-v3/include/bits/stl_bvector.h<br>
>> > @@ -149,6 +149,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER<br>
>> > { *_M_p ^= _M_mask; }<br>
>> ><br>
>> > #if __cplusplus >= 201103L<br>
>> > + // _GLIBCXX_RESOLVE_LIB_DEFECTS<br>
>> > + // 3638. vector<bool>::swap(reference, reference) is useless<br>
>> > +<br>
>> > _GLIBCXX20_CONSTEXPR<br>
>> > friend void<br>
>> > swap(_Bit_reference __x, _Bit_reference __y) noexcept<br>
>> > @@ -1266,6 +1269,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER<br>
>> > }<br>
>> ><br>
>> > // [23.2.5]/1, third-to-last entry in synopsis listing<br>
>> > + _GLIBCXX26_DEPRECATED_SUGGEST("swap' invoked via ADL or 'std::ranges::swap")<br>
>> > _GLIBCXX20_CONSTEXPR<br>
>> > static void<br>
>> > swap(reference __x, reference __y) _GLIBCXX_NOEXCEPT<br>
>> > diff --git a/libstdc++-v3/include/std/bitset b/libstdc++-v3/include/std/bitset<br>
>> > index a8cb337f4b1..a5822319b90 100644<br>
>> > --- a/libstdc++-v3/include/std/bitset<br>
>> > +++ b/libstdc++-v3/include/std/bitset<br>
>> > @@ -908,6 +908,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER<br>
>> > return *this;<br>
>> > }<br>
>> ><br>
>> > +#if __cplusplus > 202002L<br>
>><br>
>> Should we use __glibcxx_ranges_zip here? That's the macro for P2321<br>
>> which is what made the const-assignable changes that motivate this<br>
>> change.<br>
><br>
> This is matching check used for vector<bool>::reference from r13-2159-g72886f<br>
> (__cplusplus > 202002L), so I want for consistency.<br>
<br>
All the #if checks in that commit used 202002 but we later changed<br>
most of them to to use the feature test macro, in<br>
r14-8253-gae8581ea5c525b<br>
Maybe we should have changed vector<bool>::reference at the same time.<br></blockquote><div>Ah, missed that, will do a follow-up commit then. </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
OK, we have the RESOLVE_LIB_DEFECTS comment to explain why the<br>
const-assignment is present, and that issue refers to P2321 so that's<br>
good enough.<br>
<br>
OK for trunk.<br>
<br>
<br>
> Could change all of them<br>
> as separate commit. On the other hand, this is not strictly related to zip_view,<br>
> as people could write they own references.<br>
><br>
> I have wondered if the "fixes" for proxies shouldn't actually be backported<br>
> to C++20. so we support user-provided zip-views in C++20. But touching a tuple<br>
> is always scary.<br>
<br>
I don't want to make that change, it wasn't a DR it was a C++23<br>
feature. User-provided zip views just can't use std::tuple as their<br>
proxy in C++20.<br></blockquote><div>Neither I do see benefit in doing so now, but it could make sense in 2022,</div><div>when the P2321 patch landed.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
>><br>
>> If it's changed here it should be changed in the range_proxy.cc test too.<br>
><br>
> That does not work nice if we ever replace includes with modules, as we would<br>
> need to include <version>.<br>
<br>
That's the case for other tests already. In some cases the feature<br>
test macro is the right thing to check in the test, because it might<br>
depend on other conditions not only the __cplusplus value. So we might<br>
want to add <version> to affected tests, or just run the tests with<br>
-include version<br>
<br>
<br>
<br>
>><br>
>><br>
>> > + // _GLIBCXX_RESOLVE_LIB_DEFECTS<br>
>> > + // 4187. bitset::reference should be const-assignable<br>
>> > + constexpr const reference&<br>
>> > + operator=(bool __x) const noexcept<br>
>> > + {<br>
>> > + if (__x)<br>
>> > + *_M_wp |= _Base::_S_maskbit(_M_bpos);<br>
>> > + else<br>
>> > + *_M_wp &= ~_Base::_S_maskbit(_M_bpos);<br>
>> > + return *this;<br>
>> > + }<br>
>> > +#endif // C++23<br>
>> > +<br>
>> > // For b[i] = b[__j];<br>
>> > _GLIBCXX23_CONSTEXPR<br>
>> > reference&<br>
>> > @@ -939,6 +953,35 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER<br>
>> > *_M_wp ^= _Base::_S_maskbit(_M_bpos);<br>
>> > return *this;<br>
>> > }<br>
>> > +<br>
>> > +#if __cplusplus >= 201103L<br>
>><br>
>> Using 201103 here matches the condition for the<br>
>> vector<bool>::reference swap overloads, good.<br>
>><br>
>> > + _GLIBCXX23_CONSTEXPR<br>
>> > + friend void<br>
>> > + swap(reference __x, reference __y) noexcept<br>
>> > + {<br>
>> > + bool __tmp = __x;<br>
>> > + __x = __y;<br>
>> > + __y = __tmp;<br>
>> > + }<br>
>> > +<br>
>> > + _GLIBCXX23_CONSTEXPR<br>
>> > + friend void<br>
>> > + swap(reference __x, bool& __y) noexcept<br>
>> > + {<br>
>> > + bool __tmp = __x;<br>
>> > + __x = __y;<br>
>> > + __y = __tmp;<br>
>> > + }<br>
>> > +<br>
>> > + _GLIBCXX23_CONSTEXPR<br>
>> > + friend void<br>
>> > + swap(bool& __x, reference __y) noexcept<br>
>> > + {<br>
>> > + bool __tmp = __x;<br>
>> > + __x = __y;<br>
>> > + __y = __tmp;<br>
>> > + }<br>
>> > +#endif<br>
>> > };<br>
>> > friend class reference;<br>
>> ><br>
>> > diff --git a/libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc b/libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc<br>
>> > new file mode 100644<br>
>> > index 00000000000..9eac384ce44<br>
>> > --- /dev/null<br>
>> > +++ b/libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc<br>
>> > @@ -0,0 +1,94 @@<br>
>> > +// { dg-do run { target c++20 } }<br>
>> > +<br>
>> > +#include <bitset><br>
>> > +#include <concepts><br>
>> > +#include <iterator><br>
>> > +#include <testsuite_hooks.h><br>
>> > +<br>
>> > +void<br>
>> > +test_swap()<br>
>> > +{<br>
>> > + std::bitset<2> v("01");<br>
>> > + std::ranges::swap(v[0], v[1]);<br>
>> > + VERIFY( v[0] == false );<br>
>> > + VERIFY( v[1] == true );<br>
>> > +<br>
>> > + bool b = true;<br>
>> > + std::ranges::swap(v[0], b);<br>
>> > + VERIFY( v[0] == true );<br>
>> > + VERIFY( b == false );<br>
>> > +<br>
>> > + std::ranges::swap(b, v[1]);<br>
>> > + VERIFY( v[1] == false );<br>
>> > + VERIFY( b == true );<br>
>> > +}<br>
>> > +<br>
>> > +template<size_t N><br>
>> > +struct bitset_iterator<br>
>> > +{<br>
>> > + using value_type = bool;<br>
>> > + using reference = std::bitset<2>::reference;<br>
>> > + using iterator_category = std::forward_iterator_tag;<br>
>> > + using difference_type = std::ptrdiff_t;<br>
>> > +<br>
>> > + bitset_iterator() = default;<br>
>> > + bitset_iterator(std::bitset<N>& c) noexcept : cont(&c) {}<br>
>> > +<br>
>> > + reference operator*() const noexcept<br>
>> > + { return (*cont)[pos]; }<br>
>> > +<br>
>> > + bitset_iterator& operator++() {<br>
>> > + ++pos;<br>
>> > + return *this;<br>
>> > + }<br>
>> > +<br>
>> > + bitset_iterator operator++(int) {<br>
>> > + auto tmp = *this;<br>
>> > + ++pos;<br>
>> > + return *this;<br>
>> > + };<br>
>> > +<br>
>> > + friend bool operator==(bitset_iterator, bitset_iterator) = default;<br>
>> > +<br>
>> > +private:<br>
>> > + std::bitset<N>* cont = nullptr;<br>
>> > + size_t pos = 0;<br>
>> > +};<br>
>> > +<br>
>> > +void<br>
>> > +test_iter_swap()<br>
>> > +{<br>
>> > + std::bitset<2> v("01");<br>
>> > + auto it1 = bitset_iterator<2>(v);<br>
>> > + auto it2 = std::ranges::next(it1);<br>
>> > + std::ranges::iter_swap(it1, it2);<br>
>> > + VERIFY( v[0] == false );<br>
>> > + VERIFY( v[1] == true );<br>
>> > +<br>
>> > + bool b = true, *p = &b;<br>
>> > + std::ranges::iter_swap(it1, p);<br>
>> > + VERIFY( v[0] == true );<br>
>> > + VERIFY( b == false );<br>
>> > +<br>
>> > + std::ranges::iter_swap(p, it2);<br>
>> > + VERIFY( v[1] == false );<br>
>> > + VERIFY( b == true );<br>
>> > +}<br>
>> > +<br>
>> > +#if __cplusplus > 202002L<br>
>> > +static_assert( std::indirectly_writable<bitset_iterator<2>, bool> );<br>
>> > +static_assert( std::indirectly_writable<bitset_iterator<2>,<br>
>> > + std::bitset<2>::reference> );<br>
>> > +<br>
>> > +#else<br>
>> > +static_assert( !std::indirectly_writable<bitset_iterator<2>, bool> );<br>
>> > +static_assert( !std::indirectly_writable<bitset_iterator<2>,<br>
>> > + std::bitset<2>::reference> );<br>
>> > +#endif<br>
>> > +<br>
>> > +int<br>
>> > +main()<br>
>> > +{<br>
>> > + test_swap();<br>
>> > + test_iter_swap();<br>
>> > +}<br>
>> > diff --git a/libstdc++-v3/testsuite/20_util/bitset/access/ref_swap.cc b/libstdc++-v3/testsuite/20_util/bitset/access/ref_swap.cc<br>
>> > new file mode 100644<br>
>> > index 00000000000..1d8e3c1bf49<br>
>> > --- /dev/null<br>
>> > +++ b/libstdc++-v3/testsuite/20_util/bitset/access/ref_swap.cc<br>
>> > @@ -0,0 +1,28 @@<br>
>> > +// { dg-do run { target c++11 } }<br>
>> > +<br>
>> > +#include <bitset><br>
>> > +#include <testsuite_hooks.h><br>
>> > +<br>
>> > +void<br>
>> > +test_friend()<br>
>> > +{<br>
>> > + std::bitset<2> v("01");<br>
>> > + swap(v[0], v[1]);<br>
>> > + VERIFY( v[0] == false );<br>
>> > + VERIFY( v[1] == true );<br>
>> > +<br>
>> > + bool b = true;<br>
>> > + swap(v[0], b);<br>
>> > + VERIFY( v[0] == true );<br>
>> > + VERIFY( b == false );<br>
>> > +<br>
>> > + swap(b, v[1]);<br>
>> > + VERIFY( v[1] == false );<br>
>> > + VERIFY( b == true );<br>
>> > +}<br>
>> > +<br>
>> > +int<br>
>> > +main()<br>
>> > +{<br>
>> > + test_friend();<br>
>> > +}<br>
>> > diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc<br>
>> > new file mode 100644<br>
>> > index 00000000000..075132034f1<br>
>> > --- /dev/null<br>
>> > +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc<br>
>> > @@ -0,0 +1,62 @@<br>
>> > +// { dg-do run { target c++20 } }<br>
>> > +<br>
>> > +#include <vector><br>
>> > +#include <concepts><br>
>> > +#include <iterator><br>
>> > +#include <testsuite_hooks.h><br>
>> > +<br>
>> > +void<br>
>> > +test_swap()<br>
>> > +{<br>
>> > + std::vector<bool> v{true, false};<br>
>> > + std::ranges::swap(v[0], v[1]);<br>
>> > + VERIFY( v[0] == false );<br>
>> > + VERIFY( v[1] == true );<br>
>> > +<br>
>> > + bool b = true;<br>
>> > + std::ranges::swap(v[0], b);<br>
>> > + VERIFY( v[0] == true );<br>
>> > + VERIFY( b == false );<br>
>> > +<br>
>> > + std::ranges::swap(b, v[1]);<br>
>> > + VERIFY( v[1] == false );<br>
>> > + VERIFY( b == true );<br>
>> > +}<br>
>> > +<br>
>> > +void<br>
>> > +test_iter_swap()<br>
>> > +{<br>
>> > + std::vector<bool> v{true, false};<br>
>> > + auto it1 = v.begin();<br>
>> > + auto it2 = std::ranges::next(it1);<br>
>> > + std::ranges::iter_swap(it1, it2);<br>
>> > + VERIFY( v[0] == false );<br>
>> > + VERIFY( v[1] == true );<br>
>> > +<br>
>> > + bool b = true, *p = &b;<br>
>> > + std::ranges::iter_swap(it1, p);<br>
>> > + VERIFY( v[0] == true );<br>
>> > + VERIFY( b == false );<br>
>> > +<br>
>> > + std::ranges::iter_swap(p, it2);<br>
>> > + VERIFY( v[1] == false );<br>
>> > + VERIFY( b == true );<br>
>> > +}<br>
>> > +<br>
>> > +#if __cplusplus > 202002L<br>
>> > +static_assert( std::indirectly_writable<std::vector<bool>::iterator, bool> );<br>
>> > +static_assert( std::indirectly_writable<std::vector<bool>::iterator,<br>
>> > + std::vector<bool>::reference> );<br>
>> > +<br>
>> > +#else<br>
>> > +static_assert( !std::indirectly_writable<std::vector<bool>::iterator, bool> );<br>
>> > +static_assert( !std::indirectly_writable<std::vector<bool>::iterator,<br>
>> > + std::vector<bool>::reference> );<br>
>> > +#endif<br>
>> > +<br>
>> > +int<br>
>> > +main()<br>
>> > +{<br>
>> > + test_swap();<br>
>> > + test_iter_swap();<br>
>> > +}<br>
>> > diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc<br>
>> > new file mode 100644<br>
>> > index 00000000000..2221e3b03f0<br>
>> > --- /dev/null<br>
>> > +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc<br>
>> > @@ -0,0 +1,38 @@<br>
>> > +// { dg-do run { target c++11 } }<br>
>> > +<br>
>> > +#include <vector><br>
>> > +#include <testsuite_hooks.h><br>
>> > +<br>
>> > +void<br>
>> > +test_friend()<br>
>> > +{<br>
>> > + std::vector<bool> v{true, false};<br>
>> > + swap(v[0], v[1]);<br>
>> > + VERIFY( v[0] == false );<br>
>> > + VERIFY( v[1] == true );<br>
>> > +<br>
>> > + bool b = true;<br>
>> > + swap(v[0], b);<br>
>> > + VERIFY( v[0] == true );<br>
>> > + VERIFY( b == false );<br>
>> > +<br>
>> > + swap(b, v[1]);<br>
>> > + VERIFY( v[1] == false );<br>
>> > + VERIFY( b == true );<br>
>> > +}<br>
>> > +<br>
>> > +void<br>
>> > +test_static()<br>
>> > +{<br>
>> > + std::vector<bool> v{true, false};<br>
>> > + std::vector<bool>::swap(v[0], v[1]); // { dg-warning "deprecated" "::swap" { target c++26 } }<br>
>> > + VERIFY( v[0] == false );<br>
>> > + VERIFY( v[1] == true );<br>
>> > +}<br>
>> > +<br>
>> > +int<br>
>> > +main()<br>
>> > +{<br>
>> > + test_friend();<br>
>> > + test_static();<br>
>> > +}<br>
>> > diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc<br>
>> > index d2a0218a109..1eb526800f7 100644<br>
>> > --- a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc<br>
>> > +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc<br>
>> > @@ -53,7 +53,7 @@ constexpr bool<br>
>> > test_reference_swap()<br>
>> > {<br>
>> > std::vector<bool> v1 = {true, false, true};<br>
>> > - std::vector<bool>::swap(v1[0], v1[1]);<br>
>> > + std::vector<bool>::swap(v1[0], v1[1]); // { dg-warning "deprecated" "::swap" { target c++26 } }<br>
>> ><br>
>> > VERIFY(v1[0] == false);<br>
>> > VERIFY(v1[1] == true);<br>
>> > --<br>
>> > 2.54.0<br>
>> ><br>
>><br>
<br>
</blockquote></div></div>