[PATCH] libstdc++: constexpr flat_set and flat_multiset
Yuao Ma
addr2line@gmail.com
Mon Dec 8 14:20:03 GMT 2025
On Mon, Dec 8, 2025 at 6:52 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Mon, 1 Dec 2025 at 19:52, Patrick Palka <ppalka@redhat.com> wrote:
> >
> > On Wed, 26 Nov 2025, Yuao Ma wrote:
> > > diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/constexpr.cc b/libstdc++-v3/testsuite/23_containers/flat_multiset/constexpr.cc
> > > new file mode 100644
> > > index 00000000000..860b7660809
> > > --- /dev/null
> > > +++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/constexpr.cc
> > > @@ -0,0 +1,239 @@
> > > +// { dg-do run { target c++26 } }
> >
> > I'd expect constexpr tests to be compile-only.
>
> The tests have a main() function which calls VERIFY(test()), which
> only has an effect at runtime.
>
> Either they should be dg-do run, or the main() function can be removed
> and just keep:
>
> static_assert( test() );
>
> Do these new tests add any checks which are not already covered by
> existing runtime tests for flat_set and flat_multiset?
>
> If they don't add anything new, then remove the main() function and
> just do static_assert(test());
>
It turns out I misunderstood `target c++23`; I thought it would *only*
test C++23, but it actually tests against both 23 and 26. I’ve merged
the testcase and guarded it using __cplusplus > 202302L.
Please take another look, thanks!
-------------- next part --------------
From 821c14b7181940899b199ef21103efde59ebd1ae Mon Sep 17 00:00:00 2001
From: Yuao Ma <c8ef@outlook.com>
Date: Mon, 8 Dec 2025 22:14:34 +0800
Subject: [PATCH] libstdc++: constexpr flat_set and flat_multiset
This patch makes flat_set and flat_multiset constexpr as part of P3372R3.
libstdc++-v3/ChangeLog:
* include/bits/version.def: Add FTM.
* include/bits/version.h: Regenerate.
* include/std/flat_set: Add constexpr.
* testsuite/23_containers/flat_multiset/1.cc: Add constexpr test.
* testsuite/23_containers/flat_set/1.cc: Add constexpr test.
---
libstdc++-v3/include/bits/version.def | 8 ++
libstdc++-v3/include/bits/version.h | 10 ++
libstdc++-v3/include/std/flat_set | 103 +++++++++++++++++-
.../23_containers/flat_multiset/1.cc | 78 ++++++++++---
.../testsuite/23_containers/flat_set/1.cc | 79 +++++++++++---
5 files changed, 243 insertions(+), 35 deletions(-)
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 412b9ce96f8..6ff88f3072a 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1347,6 +1347,14 @@ ftms = {
};
};
+ftms = {
+ name = constexpr_flat_set;
+ values = {
+ v = 202502;
+ cxxmin = 26;
+ };
+};
+
ftms = {
name = constexpr_string;
values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 2b96934ca09..ae0df182572 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1495,6 +1495,16 @@
#endif /* !defined(__cpp_lib_constexpr_dynamic_alloc) */
#undef __glibcxx_want_constexpr_dynamic_alloc
+#if !defined(__cpp_lib_constexpr_flat_set)
+# if (__cplusplus > 202302L)
+# define __glibcxx_constexpr_flat_set 202502L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constexpr_flat_set)
+# define __cpp_lib_constexpr_flat_set 202502L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_constexpr_flat_set) */
+#undef __glibcxx_want_constexpr_flat_set
+
#if !defined(__cpp_lib_constexpr_string)
# if (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI && _GLIBCXX_HOSTED && (defined(__glibcxx_is_constant_evaluated))
# define __glibcxx_constexpr_string 201907L
diff --git a/libstdc++-v3/include/std/flat_set b/libstdc++-v3/include/std/flat_set
index c48340d7980..3a1f5ba10da 100644
--- a/libstdc++-v3/include/std/flat_set
+++ b/libstdc++-v3/include/std/flat_set
@@ -33,6 +33,7 @@
#pragma GCC system_header
#endif
+#define __glibcxx_want_constexpr_flat_set
#define __glibcxx_want_flat_set
#include <bits/version.h>
@@ -100,50 +101,60 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
container_type* _M_cont;
+ _GLIBCXX26_CONSTEXPR
_ClearGuard(container_type& __cont)
: _M_cont(std::__addressof(__cont))
{ }
+ _GLIBCXX26_CONSTEXPR
~_ClearGuard()
{
if (_M_cont)
_M_cont->clear();
}
+ _GLIBCXX26_CONSTEXPR
void
_M_disable()
{ _M_cont = nullptr; }
};
+ _GLIBCXX26_CONSTEXPR
_ClearGuard
_M_make_clear_guard()
{ return _ClearGuard{this->_M_cont}; }
public:
// constructors
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl() : _Flat_set_impl(key_compare()) { }
+ _GLIBCXX26_CONSTEXPR
explicit
_Flat_set_impl(const key_compare& __comp)
: _M_cont(), _M_comp(__comp)
{ }
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(container_type __cont, const key_compare& __comp = key_compare())
: _M_cont(std::move(__cont)), _M_comp(__comp)
{ _M_sort_uniq(); }
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t,
container_type __cont, const key_compare& __comp = key_compare())
: _M_cont(std::move(__cont)), _M_comp(__comp)
{ _GLIBCXX_DEBUG_ASSERT(ranges::is_sorted(_M_cont, _M_comp)); }
template<__has_input_iter_cat _InputIterator>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(_InputIterator __first, _InputIterator __last,
const key_compare& __comp = key_compare())
: _M_cont(), _M_comp(__comp)
{ insert(__first, __last); }
template<__has_input_iter_cat _InputIterator>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t __s,
_InputIterator __first, _InputIterator __last,
const key_compare& __comp = key_compare())
@@ -151,20 +162,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ insert(__s, __first, __last); }
template<__detail::__container_compatible_range<value_type> _Rg>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(from_range_t, _Rg&& __rg)
: _Flat_set_impl(from_range, std::forward<_Rg>(__rg), key_compare())
{ }
template<__detail::__container_compatible_range<value_type> _Rg>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(from_range_t, _Rg&& __rg, const key_compare& __comp)
: _Flat_set_impl(__comp)
{ insert_range(std::forward<_Rg>(__rg)); }
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(initializer_list<value_type> __il,
const key_compare& __comp = key_compare())
: _Flat_set_impl(__il.begin(), __il.end(), __comp)
{ }
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t __s,
initializer_list<value_type> __il,
const key_compare& __comp = key_compare())
@@ -174,23 +189,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// constructors with allocators
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
explicit
_Flat_set_impl(const _Alloc& __a)
: _Flat_set_impl(key_compare(), __a)
{ }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(const key_compare& __comp, const _Alloc& __a)
: _M_cont(std::make_obj_using_allocator<container_type>(__a)),
_M_comp(__comp)
{ }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(const container_type& __cont, const _Alloc& __a)
: _Flat_set_impl(__cont, key_compare(), __a)
{ }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(const container_type& __cont, const key_compare& __comp,
const _Alloc& __a)
: _M_cont(std::make_obj_using_allocator<container_type>(__a, __cont)),
@@ -198,11 +217,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ _M_sort_uniq(); }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t __s, const container_type& __cont, const _Alloc& __a)
: _Flat_set_impl(__s, __cont, key_compare(), __a)
{ }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t, const container_type& __cont, const key_compare& __comp,
const _Alloc& __a)
: _M_cont(std::make_obj_using_allocator<container_type>(__a, __cont)),
@@ -210,24 +231,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ _GLIBCXX_DEBUG_ASSERT(ranges::is_sorted(_M_cont, _M_comp)); }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(const _Derived& __x, const _Alloc& __a)
: _M_cont(std::make_obj_using_allocator<container_type>(__a, __x._M_cont)),
_M_comp(__x._M_comp)
{ }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(_Derived&& __x, const _Alloc& __a)
: _M_cont(std::make_obj_using_allocator<container_type>(__a, std::move(__x._M_cont))),
_M_comp(__x._M_comp)
{ }
template<__has_input_iter_cat _InputIterator, __allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(_InputIterator __first, _InputIterator __last,
const _Alloc& __a)
: _Flat_set_impl(std::move(__first), std::move(__last), key_compare(), __a)
{ }
template<__has_input_iter_cat _InputIterator, __allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(_InputIterator __first, _InputIterator __last,
const key_compare& __comp,
const _Alloc& __a)
@@ -235,6 +260,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ insert(__first, __last); }
template<__has_input_iter_cat _InputIterator, __allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t __s,
_InputIterator __first, _InputIterator __last,
const _Alloc& __a)
@@ -242,6 +268,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ }
template<__has_input_iter_cat _InputIterator, __allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t __s,
_InputIterator __first, _InputIterator __last,
const key_compare& __comp,
@@ -251,6 +278,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<__detail::__container_compatible_range<value_type> _Rg,
__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(from_range_t, _Rg&& __rg,
const _Alloc& __a)
: _Flat_set_impl(from_range, std::forward<_Rg>(__rg), key_compare(), __a)
@@ -258,6 +286,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<__detail::__container_compatible_range<value_type> _Rg,
__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(from_range_t, _Rg&& __rg,
const key_compare& __comp,
const _Alloc& __a)
@@ -265,12 +294,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ insert_range(std::forward<_Rg>(__rg)); }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(initializer_list<value_type> __il,
const _Alloc& __a)
: _Flat_set_impl(__il, key_compare(), __a)
{ }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(initializer_list<value_type> __il,
const key_compare& __comp,
const _Alloc& __a)
@@ -278,6 +309,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t __s,
initializer_list<value_type> __il,
const _Alloc& __a)
@@ -285,6 +317,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ }
template<__allocator_for<container_type> _Alloc>
+ _GLIBCXX26_CONSTEXPR
_Flat_set_impl(__sorted_t __s,
initializer_list<value_type> __il,
const key_compare& __comp,
@@ -292,6 +325,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Flat_set_impl(__s, __il.begin(), __il.end(), __comp, __a)
{ }
+ _GLIBCXX26_CONSTEXPR
_Derived&
operator=(initializer_list<value_type> __il)
{
@@ -303,54 +337,66 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
// iterators
+ _GLIBCXX26_CONSTEXPR
const_iterator
begin() const noexcept
{ return _M_cont.begin(); }
+ _GLIBCXX26_CONSTEXPR
const_iterator
end() const noexcept
{ return _M_cont.end(); }
+ _GLIBCXX26_CONSTEXPR
const_reverse_iterator
rbegin() const noexcept
{ return const_reverse_iterator(end()); }
+ _GLIBCXX26_CONSTEXPR
const_reverse_iterator
rend() const noexcept
{ return const_reverse_iterator(begin()); }
+ _GLIBCXX26_CONSTEXPR
const_iterator
cbegin() const noexcept
{ return begin(); }
+ _GLIBCXX26_CONSTEXPR
const_iterator
cend() const noexcept
{ return end(); }
+ _GLIBCXX26_CONSTEXPR
const_reverse_iterator
crbegin() const noexcept
{ return rbegin(); }
+ _GLIBCXX26_CONSTEXPR
const_reverse_iterator
crend() const noexcept
{ return rend(); }
// capacity
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
bool
empty() const noexcept
{ return _M_cont.empty(); }
+ _GLIBCXX26_CONSTEXPR
size_type
size() const noexcept
{ return _M_cont.size(); }
+ _GLIBCXX26_CONSTEXPR
size_type
max_size() const noexcept
{ return _M_cont.max_size(); }
// modifiers
template<typename _Arg, typename... _Args>
+ _GLIBCXX26_CONSTEXPR
pair<iterator, bool>
_M_try_emplace(optional<const_iterator> __hint, _Arg&& __arg, _Args&&... __args)
{
@@ -410,12 +456,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _Args>
+ _GLIBCXX26_CONSTEXPR
pair<iterator, bool>
_M_try_emplace(optional<const_iterator> __hint)
{ return _M_try_emplace(__hint, value_type()); }
template<typename... _Args>
requires is_constructible_v<value_type, _Args...>
+ _GLIBCXX26_CONSTEXPR
__emplace_result_t
emplace(_Args&&... __args)
{
@@ -427,39 +475,47 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _Args>
+ _GLIBCXX26_CONSTEXPR
iterator
emplace_hint(const_iterator __position, _Args&&... __args)
{ return _M_try_emplace(__position, std::forward<_Args>(__args)...).first; }
+ _GLIBCXX26_CONSTEXPR
__emplace_result_t
insert(const value_type& __x)
{ return emplace(__x); }
+ _GLIBCXX26_CONSTEXPR
__emplace_result_t
insert(value_type&& __x)
{ return emplace(std::move(__x)); }
+ _GLIBCXX26_CONSTEXPR
iterator
insert(const_iterator __position, const value_type& __x)
{ return emplace_hint(__position, __x); }
+ _GLIBCXX26_CONSTEXPR
iterator
insert(const_iterator __position, value_type&& __x)
{ return emplace_hint(__position, std::move(__x)); }
template<typename _Arg>
requires is_constructible_v<value_type, _Arg>
+ _GLIBCXX26_CONSTEXPR
__emplace_result_t
insert(_Arg&& __x)
{ return emplace(std::forward<_Arg>(__x)); }
template<typename _Arg>
requires is_constructible_v<value_type, _Arg>
+ _GLIBCXX26_CONSTEXPR
iterator
insert(const_iterator __position, _Arg&& __x)
{ return emplace_hint(__position, std::forward<_Arg>(__x)); }
template<__has_input_iter_cat _InputIterator>
+ _GLIBCXX26_CONSTEXPR
void
insert(_InputIterator __first, _InputIterator __last)
{
@@ -473,6 +529,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<__has_input_iter_cat _InputIterator>
+ _GLIBCXX26_CONSTEXPR
void
insert(__sorted_t, _InputIterator __first, _InputIterator __last)
{
@@ -485,6 +542,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<__detail::__container_compatible_range<value_type> _Rg>
+ _GLIBCXX26_CONSTEXPR
void
insert_range(_Rg&& __rg)
{
@@ -511,14 +569,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__guard._M_disable();
}
+ _GLIBCXX26_CONSTEXPR
void
insert(initializer_list<value_type> __il)
{ insert(__il.begin(), __il.end()); }
+ _GLIBCXX26_CONSTEXPR
void
insert(__sorted_t __s, initializer_list<value_type> __il)
{ insert(__s, __il.begin(), __il.end()); }
+ _GLIBCXX26_CONSTEXPR
container_type
extract() &&
{
@@ -526,6 +587,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return std::move(_M_cont);
}
+ _GLIBCXX26_CONSTEXPR
void
replace(container_type&& __cont)
{
@@ -535,10 +597,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__guard._M_disable();
}
+ _GLIBCXX26_CONSTEXPR
iterator
erase(const_iterator __position)
{ return _M_cont.erase(__position); }
+ _GLIBCXX26_CONSTEXPR
size_type
erase(const key_type& __x)
{ return erase<const key_type&>(__x); }
@@ -548,6 +612,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|| (__transparent_comparator<_Compare>
&& !is_convertible_v<_Key2, iterator>
&& !is_convertible_v<_Key2, const_iterator>)
+ _GLIBCXX26_CONSTEXPR
size_type
erase(_Key2&& __x)
{
@@ -557,10 +622,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __n;
}
+ _GLIBCXX26_CONSTEXPR
iterator
erase(const_iterator __first, const_iterator __last)
{ return _M_cont.erase(__first, __last); }
+ _GLIBCXX26_CONSTEXPR
void
swap(_Derived& __x) noexcept
{
@@ -569,28 +636,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
swap(_M_comp, __x._M_comp);
}
+ _GLIBCXX26_CONSTEXPR
void
clear() noexcept
{ _M_cont.clear(); }
// observers
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
key_compare
key_comp() const
{ return _M_comp; }
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
value_compare
value_comp() const
{ return _M_comp; }
// set operations
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
iterator
find(const key_type& __x)
{ return find<key_type>(__x); }
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
const_iterator
find(const key_type& __x) const
{ return find<key_type>(__x); }
@@ -598,6 +670,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
iterator
find(const _Key2& __x)
{
@@ -611,6 +684,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
const_iterator
find(const _Key2& __x) const
{
@@ -622,6 +696,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
size_type
count(const key_type& __x) const
{ return count<key_type>(__x); }
@@ -629,6 +704,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
size_type
count(const _Key2& __x) const
{
@@ -642,6 +718,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
bool
contains(const key_type& __x) const
{ return contains<key_type>(__x); }
@@ -649,16 +726,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
bool
contains(const _Key2& __x) const
{ return find(__x) != cend(); }
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
iterator
lower_bound(const key_type& __x)
{ return lower_bound<key_type>(__x); }
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
const_iterator
lower_bound(const key_type& __x) const
{ return lower_bound<key_type>(__x); }
@@ -666,6 +746,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
iterator
lower_bound(const _Key2& __x)
{ return std::lower_bound(begin(), end(), __x, _M_comp); }
@@ -673,16 +754,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
const_iterator
lower_bound(const _Key2& __x) const
{ return std::lower_bound(begin(), end(), __x, _M_comp); }
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
iterator
upper_bound(const key_type& __x)
{ return upper_bound<key_type>(__x); }
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
const_iterator
upper_bound(const key_type& __x) const
{ return upper_bound<key_type>(__x); }
@@ -690,6 +774,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
iterator
upper_bound(const _Key2& __x)
{ return std::upper_bound(begin(), end(), __x, _M_comp); }
@@ -697,16 +782,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
const_iterator
upper_bound(const _Key2& __x) const
{ return std::upper_bound(begin(), end(), __x, _M_comp); }
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
pair<iterator, iterator>
equal_range(const key_type& __x)
{ return equal_range<key_type>(__x); }
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
pair<const_iterator, const_iterator>
equal_range(const key_type& __x) const
{ return equal_range<key_type>(__x); }
@@ -714,6 +802,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
pair<iterator, iterator>
equal_range(const _Key2& __x)
{ return std::equal_range(begin(), end(), __x, _M_comp); }
@@ -721,18 +810,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key2>
requires same_as<_Key2, _Key> || __transparent_comparator<_Compare>
[[nodiscard]]
+ _GLIBCXX26_CONSTEXPR
pair<const_iterator, const_iterator>
equal_range(const _Key2& __x) const
{ return std::equal_range(begin(), end(), __x, _M_comp); }
[[nodiscard]]
- friend bool
+ friend _GLIBCXX26_CONSTEXPR bool
operator==(const _Derived& __x, const _Derived& __y)
{ return std::equal(__x.begin(), __x.end(), __y.begin(), __y.end()); }
template<typename _Up = value_type>
[[nodiscard]]
- friend __detail::__synth3way_t<_Up>
+ friend _GLIBCXX26_CONSTEXPR __detail::__synth3way_t<_Up>
operator<=>(const _Derived& __x, const _Derived& __y)
{
return std::lexicographical_compare_three_way(__x.begin(), __x.end(),
@@ -740,11 +830,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__detail::__synth3way);
}
- friend void
+ friend _GLIBCXX26_CONSTEXPR void
swap(_Derived& __x, _Derived& __y) noexcept
{ return __x.swap(__y); }
template<typename _Predicate>
+ _GLIBCXX26_CONSTEXPR
size_type
_M_erase_if(_Predicate __pred)
{
@@ -762,6 +853,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
container_type _M_cont;
[[no_unique_address]] _Compare _M_comp;
+ _GLIBCXX26_CONSTEXPR
void
_M_sort_uniq()
{
@@ -770,13 +862,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_unique();
}
+ _GLIBCXX26_CONSTEXPR
void
_M_unique() requires (!_Multi)
{
struct __key_equiv
{
+ _GLIBCXX26_CONSTEXPR
__key_equiv(key_compare __c) : _M_comp(__c) { }
+ _GLIBCXX26_CONSTEXPR
bool
operator()(const_reference __x, const_reference __y) const
{ return !_M_comp(__x, __y) && !_M_comp(__y, __x); }
@@ -934,6 +1029,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key, typename _Compare, typename _KeyContainer,
typename _Predicate>
+ _GLIBCXX26_CONSTEXPR
typename flat_set<_Key, _Compare, _KeyContainer>::size_type
erase_if(flat_set<_Key, _Compare, _KeyContainer>& __c, _Predicate __pred)
{ return __c._M_erase_if(std::move(__pred)); }
@@ -1081,6 +1177,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Key, typename _Compare, typename _KeyContainer,
typename _Predicate>
+ _GLIBCXX26_CONSTEXPR
typename flat_multiset<_Key, _Compare, _KeyContainer>::size_type
erase_if(flat_multiset<_Key, _Compare, _KeyContainer>& __c, _Predicate __pred)
{ return __c._M_erase_if(std::move(__pred)); }
diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
index 63855e07065..d1a81c65762 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
@@ -8,6 +8,7 @@
#include <testsuite_hooks.h>
template<template<class> class Sequence>
+constexpr
void
test01()
{
@@ -42,6 +43,7 @@ test01()
VERIFY( m.size() == 5 );
}
+constexpr
void
test02()
{
@@ -67,6 +69,7 @@ test02()
VERIFY( m.count(3) == 2 );
}
+constexpr
void
test03()
{
@@ -95,6 +98,7 @@ test03()
VERIFY( std::ranges::equal(m, (int[]){5}) );
}
+constexpr
void
test04()
{
@@ -121,6 +125,7 @@ test04()
VERIFY( std::move(m5).extract().get_allocator().get_personality() == 44 );
}
+constexpr
void
test05()
{
@@ -129,6 +134,7 @@ test05()
VERIFY( std::ranges::equal(m, (int[]){1, 2, 3, 3, 4, 5}) );
}
+constexpr
void
test06()
{
@@ -156,25 +162,25 @@ struct NoCatIterator {
using difference_type = int;
using value_type = int;
- NoCatIterator() : v(0) {}
- NoCatIterator(int x) : v(x) {}
+ constexpr NoCatIterator() : v(0) {}
+ constexpr NoCatIterator(int x) : v(x) {}
- int operator*() const
+ constexpr int operator*() const
{ return v; }
- NoCatIterator& operator++()
+ constexpr NoCatIterator& operator++()
{
++v;
return *this;
}
- NoCatIterator operator++(int)
+ constexpr NoCatIterator operator++(int)
{
++v;
return NoCatIterator(v-1);
}
- bool operator==(const NoCatIterator& rhs) const
+ constexpr bool operator==(const NoCatIterator& rhs) const
{ return v == rhs.v; }
private:
@@ -189,7 +195,9 @@ struct std::iterator_traits<NoCatIterator> {
// no iterator_category, happens also for common_iterator
};
-void test07()
+constexpr
+void
+test07()
{
std::flat_multiset<int> s;
std::flat_multiset<int, std::less<int>, NoInsertRange<int>> s2;
@@ -214,27 +222,39 @@ void test07()
#endif
}
+constexpr
void
test08()
{
// PR libstdc++/119620 -- flat_set::emplace always constructs element on the stack
- static int copy_counter;
+ int copy_counter = 0;
+
struct A {
- A() { }
- A(const A&) { ++copy_counter; }
- A& operator=(const A&) { ++copy_counter; return *this; }
- auto operator<=>(const A&) const = default;
+ int *counter;
+ constexpr A(int &c) : counter(&c) {}
+
+ constexpr A(const A &other) : counter(other.counter) { ++(*counter); }
+
+ constexpr A &operator=(const A &other) {
+ counter = other.counter;
+ ++(*counter);
+ return *this;
+ }
+
+ constexpr auto operator<=>(const A &) const = default;
};
+
std::vector<A> v;
v.reserve(2);
std::flat_multiset<A> s(std::move(v));
- A a;
+ A a(copy_counter);
s.emplace(a);
- VERIFY( copy_counter == 1 );
+ VERIFY(copy_counter == 1);
s.emplace(a);
- VERIFY( copy_counter == 2 );
+ VERIFY(copy_counter == 2);
}
+constexpr
void
test09()
{
@@ -245,8 +265,8 @@ test09()
VERIFY( std::ranges::equal(s, (int[]){2,2,4}) );
}
-int
-main()
+void
+test()
{
test01<std::vector>();
test01<std::deque>();
@@ -259,3 +279,27 @@ main()
test08();
test09();
}
+
+constexpr
+bool
+test_constexpr()
+{
+ test01<std::vector>();
+ test02();
+ test03();
+ test04();
+ test06();
+ test07();
+ test08();
+ test09();
+ return true;
+}
+
+int
+main()
+{
+ test();
+#if __cplusplus > 202302L
+ static_assert(test_constexpr());
+#endif
+}
diff --git a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
index b1d9002caba..65848271c07 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
@@ -6,6 +6,12 @@
# error "Feature-test macro __cpp_lib_flat_set has wrong value in <flat_set>"
#endif
+#if __cplusplus > 202302L
+#if __cpp_lib_constexpr_flat_set != 202502L
+#error "Feature-test macro __cpp_lib_constexpr_flat_set has wrong value in <flat_set>"
+#endif
+#endif
+
#include <deque>
#include <ranges>
#include <vector>
@@ -13,6 +19,7 @@
#include <testsuite_hooks.h>
template<template<typename> class KeyContainer>
+constexpr
void
test01()
{
@@ -56,6 +63,7 @@ test01()
VERIFY( m_copy == m );
}
+constexpr
void
test02()
{
@@ -82,6 +90,7 @@ test02()
VERIFY( m.count(3) == 1 );
}
+constexpr
void
test03()
{
@@ -110,6 +119,7 @@ test03()
VERIFY( std::ranges::equal(m, (int[]){5}) );
}
+constexpr
void
test04()
{
@@ -136,6 +146,7 @@ test04()
VERIFY( std::move(m5).extract().get_allocator().get_personality() == 44 );
}
+constexpr
void
test05()
{
@@ -144,6 +155,7 @@ test05()
VERIFY( std::ranges::equal(m, (int[]){1, 2, 3, 4, 5}) );
}
+constexpr
void
test06()
{
@@ -171,25 +183,25 @@ struct NoCatIterator {
using difference_type = int;
using value_type = int;
- NoCatIterator() : v(0) {}
- NoCatIterator(int x) : v(x) {}
+ constexpr NoCatIterator() : v(0) {}
+ constexpr NoCatIterator(int x) : v(x) {}
- int operator*() const
+ constexpr int operator*() const
{ return v; }
- NoCatIterator& operator++()
+ constexpr NoCatIterator& operator++()
{
++v;
return *this;
}
- NoCatIterator operator++(int)
+ constexpr NoCatIterator operator++(int)
{
++v;
return NoCatIterator(v-1);
}
- bool operator==(const NoCatIterator& rhs) const
+ constexpr bool operator==(const NoCatIterator& rhs) const
{ return v == rhs.v; }
private:
@@ -204,7 +216,9 @@ struct std::iterator_traits<NoCatIterator> {
// no iterator_category, happens also for common_iterator
};
-void test07()
+constexpr
+void
+test07()
{
std::flat_set<int> s;
std::flat_set<int, std::less<int>, NoInsertRange<int>> s2;
@@ -229,25 +243,36 @@ void test07()
#endif
}
+constexpr
void
test08()
{
// PR libstdc++/119620 -- flat_set::emplace always constructs element on the stack
- static int copy_counter;
+ int copy_counter = 0;
+
struct A {
- A() { }
- A(const A&) { ++copy_counter; }
- A& operator=(const A&) { ++copy_counter; return *this; }
- auto operator<=>(const A&) const = default;
+ int *counter;
+ constexpr A(int &c) : counter(&c) {}
+
+ constexpr A(const A &other) : counter(other.counter) { ++(*counter); }
+
+ constexpr A &operator=(const A &other) {
+ counter = other.counter;
+ ++(*counter);
+ return *this;
+ }
+
+ constexpr auto operator<=>(const A &) const = default;
};
std::flat_set<A> s;
- A a;
+ A a(copy_counter);
s.emplace(a);
VERIFY( copy_counter == 1 );
s.emplace(a);
VERIFY( copy_counter == 1 );
}
+constexpr
void
test09()
{
@@ -258,8 +283,8 @@ test09()
VERIFY( std::ranges::equal(s, (int[]){2,4}) );
}
-int
-main()
+void
+test()
{
test01<std::vector>();
test01<std::deque>();
@@ -272,3 +297,27 @@ main()
test08();
test09();
}
+
+constexpr
+bool
+test_constexpr()
+{
+ test01<std::vector>();
+ test02();
+ test03();
+ test04();
+ test06();
+ test07();
+ test08();
+ test09();
+ return true;
+}
+
+int
+main()
+{
+ test();
+#if __cplusplus > 202302L
+ static_assert(test_constexpr());
+#endif
+}
--
2.52.0
More information about the Libstdc++
mailing list