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