[PATCH v2] libstdc++: Make bitset::reference a proper range proxy.

Jonathan Wakely jwakely@redhat.com
Thu Jun 25 15:13:16 GMT 2026


On Thu, 25 Jun 2026 at 16:50 +0200, Tomasz Kamiński wrote:
>This implements remaining changes from P3612R1 Harmonize proxy-reference
>operations (LWG 3638 and 4187).
>
>As the const-assignment 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 corresponding assignment
>operator where added by P2321 in C++23 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 be 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 for both classes.
>
>The deprecation warning for static vector<bool>::swap is added since 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>::operator=(bool) const)
>	[__glibcxx_ranges_zip]: Adjust guard.
>	(vector<bool, _Alloc>::swap): Add deprected attribute via
>	_GLIBCXX26_DEPRECATED_SUGGEST.
>	* include/std/bitset (bitset::operator=(bool) const)
>	[__glibcxx_ranges_zip]: 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.
>---
>Sending v2, as full-scope test deteced that __debug::vector does not provide
>static swap overload, so guarded test and dg-warning explicitly.
>At the same time, changed the guard for const operator to use __glibcxx_ranges_zip
>as suggested, and used __glibcxx_ranges_zip in test.
>
>Testing again on x86_64-linux. *vector/bool* passed locally aslo with debug mode.
>OK for trunk when all test passes?

OK thanks

>
>
> libstdc++-v3/include/bits/stl_bvector.h       |  6 +-
> 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    | 40 ++++++++
> .../vector/bool/modifiers/swap/constexpr.cc   |  2 +-
> 7 files changed, 273 insertions(+), 2 deletions(-)
> 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..d792f707786 100644
>--- a/libstdc++-v3/include/bits/stl_bvector.h
>+++ b/libstdc++-v3/include/bits/stl_bvector.h
>@@ -116,7 +116,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>       return *this;
>     }
>
>-#if __cplusplus > 202002L
>+#if __glibcxx_ranges_zip // >= C++23
>     constexpr const _Bit_reference&
>     operator=(bool __x) const noexcept
>     {
>@@ -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..d581606e7dd 100644
>--- a/libstdc++-v3/include/std/bitset
>+++ b/libstdc++-v3/include/std/bitset
>@@ -908,6 +908,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> 	  return *this;
> 	}
>
>+#if __glibcxx_ranges_zip // >= C++23
>+	// _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
>+	_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..5949f1394cb
>--- /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 __cpp_lib_ranges_zip
>+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..21af0b9bc74
>--- /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 __cpp_lib_ranges_zip
>+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..5c250cb2bbd
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc
>@@ -0,0 +1,40 @@
>+// { 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()
>+{
>+#ifndef _GLIBCXX_DEBUG
>+  std::vector<bool> v{true, false};
>+  std::vector<bool>::swap(v[0], v[1]); // { dg-warning "deprecated" "::swap" { target { c++26 && { ! debug_mode } } } }
>+  VERIFY( v[0] == false );
>+  VERIFY( v[1] == true );
>+#endif
>+}
>+
>+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..b269b16249b 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 && { ! debug_mode } } } }
>
>   VERIFY(v1[0] == false);
>   VERIFY(v1[1] == true);
>-- 
>2.54.0
>
>



More information about the Libstdc++ mailing list