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. > This should say C++23. > > > > 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. > > If it's changed here it should be changed in the range_proxy.cc test too. > > > + // _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 > > > >
