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.

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
>

Reply via email to