[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