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
> >> >
> >>
>
>

Reply via email to