On Thu, 28 May 2026, Patrick Palka wrote:
> On Thu, 28 May 2026, Jonathan Wakely wrote:
>
> > On Wed, 27 May 2026 at 17:22, Patrick Palka <[email protected]> wrote:
> > >
> > > Changes in v2:
> > >
> > > - Bump FTM as per the paper
> > > - Fix invariant preservation of flat_meow::swap in case of exception
> > > - Use ranges::swap instead of ADL swap in flat_set::swap
> > > - Make flat_meow::swap swap the comparator last
> > > - Add missing flat_set::insert_range(sorted_foo_t, Rg&&) overload
> > > - Clean up test, check .empty() alongside .keys().empty() and
> > > .values.empty(), and don't bother doing .extract().
> > >
> > > -- >8 --
> > >
> > > This implements the changes in sections 5, 6 and 8 of P3567R2; the
> > > changes in section 4 and 7 are already effectively implemented.
> > >
> > > libstdc++-v3/ChangeLog:
> > >
> > > * include/bits/version.def (flat_map): Bump to 202511.
> > > (flat_set): Likewise.
> > > * include/bits/version.h: Regenerate.
> > > * include/std/flat_map (_Flat_map_impl): Remove
> > > is_nothrow_swappable_v assertions.
> > > (_Flat_map_impl::_Flat_map_impl): Explicitly default copy ctor.
> > > Define move ctor with corrected exception handling as per
> > > P3567R2.
> > > (_Flat_map_impl::operator=): Likewise.
> > > (_Flat_map_impl::insert_range): Define new __sorted_t overload
> > > as per P3567R2.
> > > (_Flat_map_impl::swap): Make conditionally noexcept as per
> > > P3567R2.
> > > * include/std/flat_set (_Flat_set_impl): Remove
> > > is_nothrow_swappable_v assertion.
> > > (_Flat_set_impl::_Flat_set_impl): Explicitly default copy ctor.
> > > Define move ctor with correct invariant preserving behavior as
> > > per P3567R2.
> > > (_Flat_set_impl::operator=): Likewise.
> > > (_Flat_set_impl::_M_insert_range): Factored out from
> > > insert_range. Add bool parameter __is_sorted defaulted to
> > > false.
> > > (_Flat_set_impl::insert_range): Define new __sorted_t overload
> > > as per P3567R2.
> > > (_Flat_set_impl::swap): Make conditionally noexcept as per
> > > P3567R2. Correct to use ranges::swap instead of ADL swap.
> > > * libstdc++-v3/testsuite/23_containers/flat_map/1.cc
> > > (test11, test12): New tests.
> > > * libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
> > > (test10, test11): New tests.
> > > * libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> > > (test10, test11): New tests.
> > > * libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> > > (test10, test11): New tests.
> > > ---
> > > libstdc++-v3/include/bits/version.def | 4 +-
> > > libstdc++-v3/include/bits/version.h | 8 +-
> > > libstdc++-v3/include/std/flat_map | 55 +++++++++-
> > > libstdc++-v3/include/std/flat_set | 70 ++++++++++--
> > > .../testsuite/23_containers/flat_map/1.cc | 103 +++++++++++++++++-
> > > .../23_containers/flat_multimap/1.cc | 101 +++++++++++++++++
> > > .../23_containers/flat_multiset/1.cc | 94 ++++++++++++++++
> > > .../testsuite/23_containers/flat_set/1.cc | 96 +++++++++++++++-
> > > 8 files changed, 508 insertions(+), 23 deletions(-)
> > >
> > > diff --git a/libstdc++-v3/include/bits/version.def
> > > b/libstdc++-v3/include/bits/version.def
> > > index 2f32a8bda98f..5d795151ec67 100644
> > > --- a/libstdc++-v3/include/bits/version.def
> > > +++ b/libstdc++-v3/include/bits/version.def
> > > @@ -1899,7 +1899,7 @@ ftms = {
> > > ftms = {
> > > name = flat_map;
> > > values = {
> > > - v = 202207;
> > > + v = 202511;
> > > cxxmin = 23;
> > > };
> > > };
> > > @@ -1907,7 +1907,7 @@ ftms = {
> > > ftms = {
> > > name = flat_set;
> > > values = {
> > > - v = 202207;
> > > + v = 202511;
> > > cxxmin = 23;
> > > };
> > > };
> > > diff --git a/libstdc++-v3/include/bits/version.h
> > > b/libstdc++-v3/include/bits/version.h
> > > index 517b9020ec65..c31893e34c4e 100644
> > > --- a/libstdc++-v3/include/bits/version.h
> > > +++ b/libstdc++-v3/include/bits/version.h
> > > @@ -2108,9 +2108,9 @@
> > >
> > > #if !defined(__cpp_lib_flat_map)
> > > # if (__cplusplus >= 202100L)
> > > -# define __glibcxx_flat_map 202207L
> > > +# define __glibcxx_flat_map 202511L
> > > # if defined(__glibcxx_want_all) || defined(__glibcxx_want_flat_map)
> > > -# define __cpp_lib_flat_map 202207L
> > > +# define __cpp_lib_flat_map 202511L
> > > # endif
> > > # endif
> > > #endif /* !defined(__cpp_lib_flat_map) */
> > > @@ -2118,9 +2118,9 @@
> > >
> > > #if !defined(__cpp_lib_flat_set)
> > > # if (__cplusplus >= 202100L)
> > > -# define __glibcxx_flat_set 202207L
> > > +# define __glibcxx_flat_set 202511L
> > > # if defined(__glibcxx_want_all) || defined(__glibcxx_want_flat_set)
> > > -# define __cpp_lib_flat_set 202207L
> > > +# define __cpp_lib_flat_set 202511L
> > > # endif
> > > # endif
> > > #endif /* !defined(__cpp_lib_flat_set) */
> > > diff --git a/libstdc++-v3/include/std/flat_map
> > > b/libstdc++-v3/include/std/flat_map
> > > index a51c6625d54a..18b2d24bb1f3 100644
> > > --- a/libstdc++-v3/include/std/flat_map
> > > +++ b/libstdc++-v3/include/std/flat_map
> > > @@ -74,9 +74,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > static_assert(is_same_v<_Key, typename _KeyContainer::value_type>);
> > > static_assert(is_same_v<_Tp, typename
> > > _MappedContainer::value_type>);
> > >
> > > - static_assert(is_nothrow_swappable_v<_KeyContainer>);
> > > - static_assert(is_nothrow_swappable_v<_MappedContainer>);
> > > -
> > > using _Derived = __conditional_t<_Multi,
> > > flat_multimap<_Key, _Tp, _Compare,
> > > _KeyContainer,
> > > _MappedContainer>,
> > > @@ -395,6 +392,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > : _Flat_map_impl(__s, __il.begin(), __il.end(), __comp, __a)
> > > { }
> > >
> > > + _Flat_map_impl(const _Flat_map_impl&) = default;
> > > + _Flat_map_impl& operator=(const _Flat_map_impl&) = default;
> > > +
> > > + _GLIBCXX26_CONSTEXPR
> > > + _Flat_map_impl(_Flat_map_impl&& __other)
> > > + noexcept(is_nothrow_move_constructible_v<containers>
> > > + && is_nothrow_move_constructible_v<key_compare>)
> > > + try
> > > + : _M_cont(std::move(__other._M_cont)),
> > > _M_comp(std::move(__other._M_comp))
> > > + {
> > > + __other.clear();
> > > + }
> > > + catch (...)
> > > + {
> > > + __other.clear();
> > > + throw;
> >
> > This re-throw is redundant, but if it's here for clarity (not
> > everybody is familiar with function-try-blocks) that's fine.
>
> Ah makes sense since we're inside a ctor. re-throw removed so we
> can fit each block on one line.
>
> >
> > But what happens if this is compiled with -fno-exceptions? The 'try'
> > and 'catch' and 'throw' keywords are ill-formed in that case.
>
> Oops, I forgot that -fno-exceptions makes the keywords ill-formed rather
> than silently treating thrown exceptions as aborts.
>
> >
> > It could be made to work by delegating to a new constructor that takes
> > a _ClearGuard rvalue:
> >
> > _GLIBCXX26_CONSTEXPR
> > _Flat_map_impl(_Flat_map_impl&& __other)
> > noexcept(is_nothrow_move_constructible_v<containers>
> > && is_nothrow_move_constructible_v<key_compare>)
> > : _Flat_map_impl(_ClearGuard{__other._M_cont})
> > { }
> >
> > _GLIBCXX26_CONSTEXPR
> > _Flat_map_impl(_ClearGuard __g)
> > /* don't bother repeating noexcept? */
> > : _M_cont(std::move(*__g._M_cont))
> > { }
> >
> > This will automatically clear __other when the prvalue is destroyed,
> > whether that's via an exception or not.
>
> Nice. Since there's only one affected constructor in each file I went
> with just using __cpp_exceptions to avoid introducing another ctor to
> the interface.
>
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk and perhaps 16?
>
> -- >8 --
>
> Subject: [PATCH v3 2/3] libstdc++: Implement P3567R2 flat_meow fixes
>
> Changes in v3:
> - Add comments with paper numbers to version.def
> - Guard function try-catch blocks with __cpp_exceptions
>
> Changes in v2:
>
> - Bump FTM as per the paper
> - Fix invariant preservation of flat_meow::swap in case of exception
> - Use ranges::swap instead of ADL swap in flat_set::swap
> - Make flat_meow::swap swap the comparator last
> - Add missing flat_set::insert_range(sorted_foo_t, Rg&&) overload
> - Clean up test, check .empty() alongside .keys().empty() and
> .values.empty(), and don't bother doing .extract().
>
> -- >8 --
>
> This implements the changes in sections 5, 6 and 8 of P3567R2; the
> changes in section 4 and 7 are already effectively implemented.
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/version.def (flat_map): Bump to 202511.
> (flat_set): Likewise.
> * include/bits/version.h: Regenerate.
> * include/std/flat_map (_Flat_map_impl): Remove
> is_nothrow_swappable_v assertions.
> (_Flat_map_impl::_Flat_map_impl): Explicitly default copy ctor.
> Define move ctor with corrected exception handling as per
> P3567R2.
> (_Flat_map_impl::operator=): Likewise.
> (_Flat_map_impl::insert_range): Define new __sorted_t overload
> as per P3567R2.
> (_Flat_map_impl::swap): Make conditionally noexcept as per
> P3567R2.
> * include/std/flat_set (_Flat_set_impl): Remove
> is_nothrow_swappable_v assertion.
> (_Flat_set_impl::_Flat_set_impl): Explicitly default copy ctor.
> Define move ctor with correct invariant preserving behavior as
> per P3567R2.
> (_Flat_set_impl::operator=): Likewise.
> (_Flat_set_impl::_M_insert_range): Factored out from
> insert_range. Add bool parameter __is_sorted defaulted to
> false.
> (_Flat_set_impl::insert_range): Define new __sorted_t overload
> as per P3567R2.
> (_Flat_set_impl::swap): Make conditionally noexcept as per
> P3567R2. Correct to use ranges::swap instead of ADL swap.
> * libstdc++-v3/testsuite/23_containers/flat_map/1.cc
> (test11, test12): New tests.
> * libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
> (test10, test11): New tests.
> * libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
> (test10, test11): New tests.
> * libstdc++-v3/testsuite/23_containers/flat_set/1.cc
> (test10, test11): New tests.
>
> Reviewed-by: Tomasz Kamiński <[email protected]>
> Reviewed-by: Jonathan Wakely <[email protected]>
> ---
> libstdc++-v3/include/bits/version.def | 9 +-
> libstdc++-v3/include/bits/version.h | 8 +-
> libstdc++-v3/include/std/flat_map | 54 ++++++++-
> libstdc++-v3/include/std/flat_set | 69 ++++++++++--
> .../testsuite/23_containers/flat_map/1.cc | 104 +++++++++++++++++-
> .../23_containers/flat_multimap/1.cc | 102 +++++++++++++++++
> .../23_containers/flat_multiset/1.cc | 94 ++++++++++++++++
> .../testsuite/23_containers/flat_set/1.cc | 96 +++++++++++++++-
> 8 files changed, 513 insertions(+), 23 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> index 2f32a8bda98f..97f7d6865190 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1898,16 +1898,21 @@ ftms = {
>
> ftms = {
> name = flat_map;
> + // 202207 P0429R9 A Standard flat_map
> + // 202511 P3567R2 flat_meow Fixes
> values = {
> - v = 202207;
> + v = 202511; // P3567R2 applied as DR for C++23
> cxxmin = 23;
> };
> };
>
> ftms = {
> name = flat_set;
> + // 202207 P1222R4 A Standard flat_set
> + // LWG3751 Missing feature macro for flat_set
> + // 202511 P3567R2 flat_meow Fixes
> values = {
> - v = 202207;
> + v = 202511; // P3567R2 applied as DR for C++23
> cxxmin = 23;
> };
> };
> diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> index 517b9020ec65..c31893e34c4e 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2108,9 +2108,9 @@
>
> #if !defined(__cpp_lib_flat_map)
> # if (__cplusplus >= 202100L)
> -# define __glibcxx_flat_map 202207L
> +# define __glibcxx_flat_map 202511L
> # if defined(__glibcxx_want_all) || defined(__glibcxx_want_flat_map)
> -# define __cpp_lib_flat_map 202207L
> +# define __cpp_lib_flat_map 202511L
> # endif
> # endif
> #endif /* !defined(__cpp_lib_flat_map) */
> @@ -2118,9 +2118,9 @@
>
> #if !defined(__cpp_lib_flat_set)
> # if (__cplusplus >= 202100L)
> -# define __glibcxx_flat_set 202207L
> +# define __glibcxx_flat_set 202511L
> # if defined(__glibcxx_want_all) || defined(__glibcxx_want_flat_set)
> -# define __cpp_lib_flat_set 202207L
> +# define __cpp_lib_flat_set 202511L
> # endif
> # endif
> #endif /* !defined(__cpp_lib_flat_set) */
> diff --git a/libstdc++-v3/include/std/flat_map
> b/libstdc++-v3/include/std/flat_map
> index a51c6625d54a..f06c75a16f65 100644
> --- a/libstdc++-v3/include/std/flat_map
> +++ b/libstdc++-v3/include/std/flat_map
> @@ -74,9 +74,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> static_assert(is_same_v<_Key, typename _KeyContainer::value_type>);
> static_assert(is_same_v<_Tp, typename _MappedContainer::value_type>);
>
> - static_assert(is_nothrow_swappable_v<_KeyContainer>);
> - static_assert(is_nothrow_swappable_v<_MappedContainer>);
> -
> using _Derived = __conditional_t<_Multi,
> flat_multimap<_Key, _Tp, _Compare,
> _KeyContainer,
> _MappedContainer>,
> @@ -395,6 +392,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> : _Flat_map_impl(__s, __il.begin(), __il.end(), __comp, __a)
> { }
>
> + _Flat_map_impl(const _Flat_map_impl&) = default;
> + _Flat_map_impl& operator=(const _Flat_map_impl&) = default;
> +
> + _GLIBCXX26_CONSTEXPR
> + _Flat_map_impl(_Flat_map_impl&& __other)
> + noexcept(is_nothrow_move_constructible_v<containers>
> + && is_nothrow_move_constructible_v<key_compare>)
> +#if __cpp_exceptions
> + try
> +#endif
> + : _M_cont(std::move(__other._M_cont)),
> _M_comp(std::move(__other._M_comp))
> + { __other.clear(); }
> +#if __cpp_exceptions
> + catch (...)
> + { __other.clear(); }
> +#endif
> +
> + _GLIBCXX26_CONSTEXPR
> + _Flat_map_impl&
> + operator=(_Flat_map_impl&& __other)
> + noexcept(is_nothrow_move_assignable_v<containers>
> + && is_nothrow_move_assignable_v<key_compare>)
> + {
> + auto __guard = _M_make_clear_guard();
> + auto __guard_other = _ClearGuard{__other._M_cont};
> + _M_cont = std::move(__other._M_cont);
> + _M_comp = std::move(__other._M_comp);
> + __guard._M_disable();
> + // __guard_other._M_disable is deliberately not called.
> + return *this;
> + }
> +
> _GLIBCXX26_CONSTEXPR
> _Derived&
> operator=(initializer_list<value_type> __il)
> @@ -644,6 +673,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> insert_range(_Rg&& __rg)
> { _M_insert(ranges::begin(__rg), ranges::end(__rg)); }
>
> + template<__detail::__container_compatible_range<value_type> _Rg>
> + _GLIBCXX26_CONSTEXPR
> + void
> + insert_range(__sorted_t, _Rg&& __rg)
> + { _M_insert(ranges::begin(__rg), ranges::end(__rg), true); }
> +
> _GLIBCXX26_CONSTEXPR
> void
> insert(initializer_list<value_type> __il)
> @@ -728,11 +763,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> _GLIBCXX26_CONSTEXPR
> void
> - swap(_Derived& __y) noexcept
> + swap(_Derived& __y)
> + noexcept(is_nothrow_swappable_v<key_container_type>
> + && is_nothrow_swappable_v<mapped_container_type>
> + && is_nothrow_swappable_v<key_compare>)
> {
> - ranges::swap(_M_comp, __y._M_comp);
> + auto __guard = _M_make_clear_guard();
> + auto __guard_y = _ClearGuard{__y._M_cont};
> ranges::swap(_M_cont.keys, __y._M_cont.keys);
> ranges::swap(_M_cont.values, __y._M_cont.values);
> + ranges::swap(_M_comp, __y._M_comp);
> + __guard._M_disable();
> + __guard_y._M_disable();
> }
>
> _GLIBCXX26_CONSTEXPR
> @@ -974,7 +1016,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> friend _GLIBCXX26_CONSTEXPR void
> - swap(_Derived& __x, _Derived& __y) noexcept
> + swap(_Derived& __x, _Derived& __y) noexcept(noexcept(__x.swap(__y)))
> { return __x.swap(__y); }
>
> template<typename _Predicate>
> diff --git a/libstdc++-v3/include/std/flat_set
> b/libstdc++-v3/include/std/flat_set
> index 286290896ae0..c6c77dd4cbcb 100644
> --- a/libstdc++-v3/include/std/flat_set
> +++ b/libstdc++-v3/include/std/flat_set
> @@ -71,7 +71,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> class _Flat_set_impl
> {
> static_assert(is_same_v<_Key, typename _KeyContainer::value_type>);
> - static_assert(is_nothrow_swappable_v<_KeyContainer>);
>
> using _Derived = __conditional_t<_Multi,
> flat_multiset<_Key, _Compare,
> _KeyContainer>,
> @@ -324,6 +323,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> : _Flat_set_impl(__s, __il.begin(), __il.end(), __comp, __a)
> { }
>
> + _Flat_set_impl(const _Flat_set_impl&) = default;
> + _Flat_set_impl& operator=(const _Flat_set_impl&) = default;
> +
> + _GLIBCXX26_CONSTEXPR
> + _Flat_set_impl(_Flat_set_impl&& __other)
> + noexcept(is_nothrow_move_constructible_v<container_type>
> + && is_nothrow_move_constructible_v<key_compare>)
> +#if __cpp_exceptions
> + try
> +#endif
> + : _M_cont(std::move(__other._M_cont)),
> _M_comp(std::move(__other._M_comp))
> + { __other.clear(); }
> +#if __cpp_exceptions
> + catch (...)
> + { __other.clear(); }
> +#endif
> +
> + _GLIBCXX26_CONSTEXPR
> + _Flat_set_impl&
> + operator=(_Flat_set_impl&& __other)
> + noexcept(is_nothrow_move_assignable_v<container_type>
> + && is_nothrow_move_assignable_v<key_compare>)
> + {
> + auto __guard = _M_make_clear_guard();
> + auto __guard_other = _ClearGuard{__other._M_cont};
> + _M_cont = std::move(__other._M_cont);
> + _M_comp = std::move(__other._M_comp);
> + __guard._M_disable();
> + // __guard_other._M_disable is deliberately not called.
> + return *this;
> + }
> +
> _GLIBCXX26_CONSTEXPR
> _Derived&
> operator=(initializer_list<value_type> __il)
> @@ -540,10 +571,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __guard._M_disable();
> }
>
> - template<__detail::__container_compatible_range<value_type> _Rg>
> + template<typename _Rg>
> _GLIBCXX26_CONSTEXPR
> void
> - insert_range(_Rg&& __rg)
> + _M_insert_range(_Rg&& __rg, bool __is_sorted = false)
> {
> auto __guard = _M_make_clear_guard();
> typename container_type::iterator __it;
> @@ -561,13 +592,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _M_cont.emplace_back(*__first);
> __it = _M_cont.begin() + __n;
> }
> - std::sort(__it, _M_cont.end(), _M_comp);
> + if (__is_sorted)
> + _GLIBCXX_DEBUG_ASSERT(ranges::is_sorted(_M_cont, _M_comp));
Oops, this assertion is checking the entire underlying container (with
the incoming range appended to it) is sorted, it should be checking
that only the incoming range is sorted:
_GLIBCXX_DEBUG_ASSERT(ranges::is_sorted(__it, _M_cont.end(), _M_comp));
Fixed and extended the tests to cover this situation.
-- >8 --
Subject: [PATCH v4 2/3] libstdc++: Implement P3567R2 flat_meow fixes
Changes in v4:
- Fix _GLIBCXX_DEBUG_ASSERT in _Flat_set_impl::_M_insert_range.
Changes in v3:
- Add comments with paper numbers to version.def
- Guard function try-catch blocks with __cpp_exceptions
Changes in v2:
- Bump FTM as per the paper
- Fix invariant preservation of flat_meow::swap in case of exception
- Use ranges::swap instead of ADL swap in flat_set::swap
- Make flat_meow::swap swap the comparator last
- Add missing flat_set::insert_range(sorted_foo_t, Rg&&) overload
- Clean up test, check .empty() alongside .keys().empty() and
.values.empty(), and don't bother doing .extract().
-- >8 --
This implements the changes in sections 5, 6 and 8 of P3567R2; the
changes in section 4 and 7 are already effectively implemented.
libstdc++-v3/ChangeLog:
* include/bits/version.def (flat_map): Bump to 202511.
(flat_set): Likewise.
* include/bits/version.h: Regenerate.
* include/std/flat_map (_Flat_map_impl): Remove
is_nothrow_swappable_v assertions.
(_Flat_map_impl::_Flat_map_impl): Explicitly default copy ctor.
Define move ctor with corrected exception handling as per
P3567R2.
(_Flat_map_impl::operator=): Likewise.
(_Flat_map_impl::insert_range): Define new __sorted_t overload
as per P3567R2.
(_Flat_map_impl::swap): Make conditionally noexcept as per
P3567R2.
* include/std/flat_set (_Flat_set_impl): Remove
is_nothrow_swappable_v assertion.
(_Flat_set_impl::_Flat_set_impl): Explicitly default copy ctor.
Define move ctor with correct invariant preserving behavior as
per P3567R2.
(_Flat_set_impl::operator=): Likewise.
(_Flat_set_impl::_M_insert_range): Factored out from
insert_range. Add bool parameter __is_sorted defaulted to
false.
(_Flat_set_impl::insert_range): Define new __sorted_t overload
as per P3567R2.
(_Flat_set_impl::swap): Make conditionally noexcept as per
P3567R2. Correct to use ranges::swap instead of ADL swap.
* libstdc++-v3/testsuite/23_containers/flat_map/1.cc
(test11, test12): New tests.
* libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
(test10, test11): New tests.
* libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
(test10, test11): New tests.
* libstdc++-v3/testsuite/23_containers/flat_set/1.cc
(test10, test11): New tests.
Reviewed-by: Tomasz Kamiński <[email protected]>
Reviewed-by: Jonathan Wakely <[email protected]>
---
libstdc++-v3/include/bits/version.def | 9 +-
libstdc++-v3/include/bits/version.h | 8 +-
libstdc++-v3/include/std/flat_map | 54 ++++++++-
libstdc++-v3/include/std/flat_set | 69 ++++++++++--
.../testsuite/23_containers/flat_map/1.cc | 104 +++++++++++++++++-
.../23_containers/flat_multimap/1.cc | 102 +++++++++++++++++
.../23_containers/flat_multiset/1.cc | 94 ++++++++++++++++
.../testsuite/23_containers/flat_set/1.cc | 96 +++++++++++++++-
8 files changed, 513 insertions(+), 23 deletions(-)
diff --git a/libstdc++-v3/include/bits/version.def
b/libstdc++-v3/include/bits/version.def
index 2f32a8bda98f..97f7d6865190 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1898,16 +1898,21 @@ ftms = {
ftms = {
name = flat_map;
+ // 202207 P0429R9 A Standard flat_map
+ // 202511 P3567R2 flat_meow Fixes
values = {
- v = 202207;
+ v = 202511; // P3567R2 applied as DR for C++23
cxxmin = 23;
};
};
ftms = {
name = flat_set;
+ // 202207 P1222R4 A Standard flat_set
+ // LWG3751 Missing feature macro for flat_set
+ // 202511 P3567R2 flat_meow Fixes
values = {
- v = 202207;
+ v = 202511; // P3567R2 applied as DR for C++23
cxxmin = 23;
};
};
diff --git a/libstdc++-v3/include/bits/version.h
b/libstdc++-v3/include/bits/version.h
index 517b9020ec65..c31893e34c4e 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2108,9 +2108,9 @@
#if !defined(__cpp_lib_flat_map)
# if (__cplusplus >= 202100L)
-# define __glibcxx_flat_map 202207L
+# define __glibcxx_flat_map 202511L
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_flat_map)
-# define __cpp_lib_flat_map 202207L
+# define __cpp_lib_flat_map 202511L
# endif
# endif
#endif /* !defined(__cpp_lib_flat_map) */
@@ -2118,9 +2118,9 @@
#if !defined(__cpp_lib_flat_set)
# if (__cplusplus >= 202100L)
-# define __glibcxx_flat_set 202207L
+# define __glibcxx_flat_set 202511L
# if defined(__glibcxx_want_all) || defined(__glibcxx_want_flat_set)
-# define __cpp_lib_flat_set 202207L
+# define __cpp_lib_flat_set 202511L
# endif
# endif
#endif /* !defined(__cpp_lib_flat_set) */
diff --git a/libstdc++-v3/include/std/flat_map
b/libstdc++-v3/include/std/flat_map
index a51c6625d54a..f06c75a16f65 100644
--- a/libstdc++-v3/include/std/flat_map
+++ b/libstdc++-v3/include/std/flat_map
@@ -74,9 +74,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static_assert(is_same_v<_Key, typename _KeyContainer::value_type>);
static_assert(is_same_v<_Tp, typename _MappedContainer::value_type>);
- static_assert(is_nothrow_swappable_v<_KeyContainer>);
- static_assert(is_nothrow_swappable_v<_MappedContainer>);
-
using _Derived = __conditional_t<_Multi,
flat_multimap<_Key, _Tp, _Compare,
_KeyContainer,
_MappedContainer>,
@@ -395,6 +392,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Flat_map_impl(__s, __il.begin(), __il.end(), __comp, __a)
{ }
+ _Flat_map_impl(const _Flat_map_impl&) = default;
+ _Flat_map_impl& operator=(const _Flat_map_impl&) = default;
+
+ _GLIBCXX26_CONSTEXPR
+ _Flat_map_impl(_Flat_map_impl&& __other)
+ noexcept(is_nothrow_move_constructible_v<containers>
+ && is_nothrow_move_constructible_v<key_compare>)
+#if __cpp_exceptions
+ try
+#endif
+ : _M_cont(std::move(__other._M_cont)),
_M_comp(std::move(__other._M_comp))
+ { __other.clear(); }
+#if __cpp_exceptions
+ catch (...)
+ { __other.clear(); }
+#endif
+
+ _GLIBCXX26_CONSTEXPR
+ _Flat_map_impl&
+ operator=(_Flat_map_impl&& __other)
+ noexcept(is_nothrow_move_assignable_v<containers>
+ && is_nothrow_move_assignable_v<key_compare>)
+ {
+ auto __guard = _M_make_clear_guard();
+ auto __guard_other = _ClearGuard{__other._M_cont};
+ _M_cont = std::move(__other._M_cont);
+ _M_comp = std::move(__other._M_comp);
+ __guard._M_disable();
+ // __guard_other._M_disable is deliberately not called.
+ return *this;
+ }
+
_GLIBCXX26_CONSTEXPR
_Derived&
operator=(initializer_list<value_type> __il)
@@ -644,6 +673,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
insert_range(_Rg&& __rg)
{ _M_insert(ranges::begin(__rg), ranges::end(__rg)); }
+ template<__detail::__container_compatible_range<value_type> _Rg>
+ _GLIBCXX26_CONSTEXPR
+ void
+ insert_range(__sorted_t, _Rg&& __rg)
+ { _M_insert(ranges::begin(__rg), ranges::end(__rg), true); }
+
_GLIBCXX26_CONSTEXPR
void
insert(initializer_list<value_type> __il)
@@ -728,11 +763,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX26_CONSTEXPR
void
- swap(_Derived& __y) noexcept
+ swap(_Derived& __y)
+ noexcept(is_nothrow_swappable_v<key_container_type>
+ && is_nothrow_swappable_v<mapped_container_type>
+ && is_nothrow_swappable_v<key_compare>)
{
- ranges::swap(_M_comp, __y._M_comp);
+ auto __guard = _M_make_clear_guard();
+ auto __guard_y = _ClearGuard{__y._M_cont};
ranges::swap(_M_cont.keys, __y._M_cont.keys);
ranges::swap(_M_cont.values, __y._M_cont.values);
+ ranges::swap(_M_comp, __y._M_comp);
+ __guard._M_disable();
+ __guard_y._M_disable();
}
_GLIBCXX26_CONSTEXPR
@@ -974,7 +1016,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
friend _GLIBCXX26_CONSTEXPR void
- swap(_Derived& __x, _Derived& __y) noexcept
+ swap(_Derived& __x, _Derived& __y) noexcept(noexcept(__x.swap(__y)))
{ return __x.swap(__y); }
template<typename _Predicate>
diff --git a/libstdc++-v3/include/std/flat_set
b/libstdc++-v3/include/std/flat_set
index 286290896ae0..e92559949ebb 100644
--- a/libstdc++-v3/include/std/flat_set
+++ b/libstdc++-v3/include/std/flat_set
@@ -71,7 +71,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
class _Flat_set_impl
{
static_assert(is_same_v<_Key, typename _KeyContainer::value_type>);
- static_assert(is_nothrow_swappable_v<_KeyContainer>);
using _Derived = __conditional_t<_Multi,
flat_multiset<_Key, _Compare,
_KeyContainer>,
@@ -324,6 +323,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _Flat_set_impl(__s, __il.begin(), __il.end(), __comp, __a)
{ }
+ _Flat_set_impl(const _Flat_set_impl&) = default;
+ _Flat_set_impl& operator=(const _Flat_set_impl&) = default;
+
+ _GLIBCXX26_CONSTEXPR
+ _Flat_set_impl(_Flat_set_impl&& __other)
+ noexcept(is_nothrow_move_constructible_v<container_type>
+ && is_nothrow_move_constructible_v<key_compare>)
+#if __cpp_exceptions
+ try
+#endif
+ : _M_cont(std::move(__other._M_cont)),
_M_comp(std::move(__other._M_comp))
+ { __other.clear(); }
+#if __cpp_exceptions
+ catch (...)
+ { __other.clear(); }
+#endif
+
+ _GLIBCXX26_CONSTEXPR
+ _Flat_set_impl&
+ operator=(_Flat_set_impl&& __other)
+ noexcept(is_nothrow_move_assignable_v<container_type>
+ && is_nothrow_move_assignable_v<key_compare>)
+ {
+ auto __guard = _M_make_clear_guard();
+ auto __guard_other = _ClearGuard{__other._M_cont};
+ _M_cont = std::move(__other._M_cont);
+ _M_comp = std::move(__other._M_comp);
+ __guard._M_disable();
+ // __guard_other._M_disable is deliberately not called.
+ return *this;
+ }
+
_GLIBCXX26_CONSTEXPR
_Derived&
operator=(initializer_list<value_type> __il)
@@ -540,10 +571,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__guard._M_disable();
}
- template<__detail::__container_compatible_range<value_type> _Rg>
+ template<typename _Rg>
_GLIBCXX26_CONSTEXPR
void
- insert_range(_Rg&& __rg)
+ _M_insert_range(_Rg&& __rg, bool __is_sorted = false)
{
auto __guard = _M_make_clear_guard();
typename container_type::iterator __it;
@@ -561,13 +592,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_cont.emplace_back(*__first);
__it = _M_cont.begin() + __n;
}
- std::sort(__it, _M_cont.end(), _M_comp);
+ if (__is_sorted)
+ _GLIBCXX_DEBUG_ASSERT(ranges::is_sorted(__it, _M_cont.end(),
_M_comp));
+ else
+ std::sort(__it, _M_cont.end(), _M_comp);
std::inplace_merge(_M_cont.begin(), __it, _M_cont.end(), _M_comp);
if constexpr (!_Multi)
_M_unique();
__guard._M_disable();
}
+ template<__detail::__container_compatible_range<value_type> _Rg>
+ _GLIBCXX26_CONSTEXPR
+ void
+ insert_range(_Rg&& __rg)
+ { _M_insert_range(std::forward<_Rg>(__rg)); }
+
+ template<__detail::__container_compatible_range<value_type> _Rg>
+ _GLIBCXX26_CONSTEXPR
+ void
+ insert_range(__sorted_t, _Rg&& __rg)
+ { _M_insert_range(std::forward<_Rg>(__rg), true); }
+
_GLIBCXX26_CONSTEXPR
void
insert(initializer_list<value_type> __il)
@@ -628,11 +674,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX26_CONSTEXPR
void
- swap(_Derived& __x) noexcept
+ swap(_Derived& __y)
+ noexcept(is_nothrow_swappable_v<container_type>
+ && is_nothrow_swappable_v<key_compare>)
{
- using std::swap;
- swap(_M_cont, __x._M_cont);
- swap(_M_comp, __x._M_comp);
+ auto __guard = _M_make_clear_guard();
+ auto __guard_y = _ClearGuard{__y._M_cont};
+ ranges::swap(_M_comp, __y._M_comp);
+ ranges::swap(_M_cont, __y._M_cont);
+ __guard._M_disable();
+ __guard_y._M_disable();
}
_GLIBCXX26_CONSTEXPR
@@ -830,7 +881,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
friend _GLIBCXX26_CONSTEXPR void
- swap(_Derived& __x, _Derived& __y) noexcept
+ swap(_Derived& __x, _Derived& __y) noexcept(noexcept(__x.swap(__y)))
{ return __x.swap(__y); }
template<typename _Predicate>
diff --git a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
index 1cb088f511eb..7e22e0366ec2 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc
@@ -3,7 +3,7 @@
#include <flat_map>
-#if __cpp_lib_flat_map != 202207L
+#if __cpp_lib_flat_map != 202511L
# error "Feature-test macro __cpp_lib_flat_map has wrong value in <flat_map>"
#endif
@@ -299,6 +299,103 @@ test10()
VERIFY (k == "world");
}
+template<typename T>
+struct throwing_vector : std::vector<T>
+{
+ static inline bool throw_on_move = false;
+
+ throwing_vector() = default;
+ throwing_vector(const throwing_vector&) = default;
+ throwing_vector& operator=(const throwing_vector&) = default;
+
+ throwing_vector(throwing_vector&& other)
+ : std::vector<T>(std::move(other))
+ {
+ if (throw_on_move)
+ throw std::runtime_error("move ctor");
+ }
+
+ throwing_vector&
+ operator=(throwing_vector&& other)
+ {
+ static_cast<std::vector<T>&>(*this) = std::move(other);
+ if (throw_on_move)
+ throw std::runtime_error("move assign");
+ return *this;
+ }
+};
+
+template<template<typename> class KC, template<typename> class MC>
+void
+test11()
+{
+#if __cpp_exceptions
+ using flat_map = std::flat_map<int, int, std::less<int>, KC<int>, MC<int>>;
+
+ auto is_really_empty = [](const flat_map& m) {
+ return m.empty() && m.keys().empty() && m.values().empty();
+ };
+ throwing_vector<int>::throw_on_move = true;
+
+ // Verify invariant preservation upon throwing move construction.
+ flat_map source;
+ source.insert({{1, 100}, {2, 200}});
+ try
+ {
+ flat_map target(std::move(source));
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( is_really_empty(source) );
+ }
+
+ // Verify invariant preservation upon throwing move assignment.
+ source.clear();
+ source.insert({{1, 100}, {2, 200}});
+ flat_map target;
+ target.insert({{3, 300}, {4, 400}});
+ try
+ {
+ target = std::move(source);
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( is_really_empty(source) );
+ VERIFY( is_really_empty(target) );
+ }
+
+ // Verify invariant preservation upon throwing swap.
+ source.clear();
+ source.insert({{1, 100}, {2, 200}});
+ target.clear();
+ target.insert({{3, 300}, {4, 400}});
+ try
+ {
+ source.swap(target);
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( is_really_empty(source) );
+ VERIFY( is_really_empty(target) );
+ }
+#endif
+}
+
+constexpr
+void
+test12()
+{
+ // Verify usability of flat_map::insert_range(sorted_unique_t, Rg&&).
+ std::flat_map<int, int> m = {{2, 200}};
+ std::pair<int, int> s[] = {{1, 100}, {3, 300}};
+ m.insert_range(std::sorted_unique, s);
+ VERIFY( std::ranges::equal(m.keys(), (int[]){1, 2, 3}) );
+ VERIFY( std::ranges::equal(m.values(), (int[]){100, 200, 300}) );
+}
+
void
test()
{
@@ -315,6 +412,9 @@ test()
test08();
test09();
test10();
+ test11<std::vector, throwing_vector>();
+ test11<throwing_vector, std::vector>();
+ test12();
}
constexpr
@@ -333,6 +433,8 @@ test_constexpr()
#if __cpp_lib_constexpr_string >= 201907L
test10();
#endif
+ // test11() is non-constexpr
+ test12();
return true;
}
diff --git a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
index 00644c14061a..48688b7583ed 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc
@@ -252,6 +252,103 @@ test09()
using value_type = std::pair<int, int>;
}
+template<typename T>
+struct throwing_vector : std::vector<T>
+{
+ static inline bool throw_on_move = false;
+
+ throwing_vector() = default;
+ throwing_vector(const throwing_vector&) = default;
+ throwing_vector& operator=(const throwing_vector&) = default;
+
+ throwing_vector(throwing_vector&& other)
+ : std::vector<T>(std::move(other))
+ {
+ if (throw_on_move)
+ throw std::runtime_error("move ctor");
+ }
+
+ throwing_vector&
+ operator=(throwing_vector&& other)
+ {
+ static_cast<std::vector<T>&>(*this) = std::move(other);
+ if (throw_on_move)
+ throw std::runtime_error("move assign");
+ return *this;
+ }
+};
+
+template<template<typename> class KC, template<typename> class MC>
+void
+test10()
+{
+#if __cpp_exceptions
+ using flat_multimap = std::flat_multimap<int, int, std::less<int>, KC<int>,
MC<int>>;
+
+ auto is_really_empty = [](const flat_multimap& m) {
+ return m.empty() && m.keys().empty() && m.values().empty();
+ };
+ throwing_vector<int>::throw_on_move = true;
+
+ // Verify invariant preservation upon throwing move construction.
+ flat_multimap source;
+ source.insert({{1, 100}, {2, 200}});
+ try
+ {
+ flat_multimap target(std::move(source));
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( is_really_empty(source) );
+ }
+
+ // Verify invariant preservation upon throwing move assignment.
+ source.clear();
+ source.insert({{1, 100}, {2, 200}});
+ flat_multimap target;
+ target.insert({{3, 300}, {4, 400}});
+ try
+ {
+ target = std::move(source);
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( is_really_empty(source) );
+ VERIFY( is_really_empty(target) );
+ }
+
+ // Verify invariant preservation upon throwing swap.
+ source.clear();
+ source.insert({{1, 100}, {2, 200}});
+ target.clear();
+ target.insert({{3, 300}, {4, 400}});
+ try
+ {
+ source.swap(target);
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( is_really_empty(source) );
+ VERIFY( is_really_empty(target) );
+ }
+#endif
+}
+
+constexpr
+void
+test11()
+{
+ // Verify usability of flat_multimap::insert_range(sorted_equivalent_t,
Rg&&).
+ std::flat_multimap<int, int> m = {{2, 200}};;
+ std::pair<int, int> s[] = {{1, 100}, {3, 300}};
+ m.insert_range(std::sorted_equivalent, s);
+ VERIFY( std::ranges::equal(m.keys(), (int[]){1, 2, 3}) );
+ VERIFY( std::ranges::equal(m.values(), (int[]){100, 200, 300}) );
+}
+
void
test()
{
@@ -266,6 +363,9 @@ test()
test06();
test07();
test09();
+ test10<std::vector, throwing_vector>();
+ test10<throwing_vector, std::vector>();
+ test11();
}
constexpr
@@ -280,6 +380,8 @@ test_constexpr()
test06();
test07();
test09();
+ // test10() is non-constexpr
+ test11();
return true;
}
diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
index be330fefdd05..4544adb50770 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc
@@ -265,6 +265,96 @@ test09()
VERIFY( std::ranges::equal(s, (int[]){2,2,4}) );
}
+template<typename T>
+struct throwing_vector : std::vector<T>
+{
+ static inline bool throw_on_move = false;
+
+ throwing_vector() = default;
+ throwing_vector(const throwing_vector&) = default;
+ throwing_vector& operator=(const throwing_vector&) = default;
+
+ throwing_vector(throwing_vector&& other)
+ : std::vector<T>(std::move(other))
+ {
+ if (throw_on_move)
+ throw std::runtime_error("move ctor");
+ }
+
+ throwing_vector&
+ operator=(throwing_vector&& other)
+ {
+ static_cast<std::vector<T>&>(*this) = std::move(other);
+ if (throw_on_move)
+ throw std::runtime_error("move assign");
+ return *this;
+ }
+};
+
+void
+test10()
+{
+#if __cpp_exceptions
+ using flat_multiset = std::flat_multiset<int, std::less<int>,
throwing_vector<int>>;
+
+ throwing_vector<int>::throw_on_move = true;
+
+ // Verify invariant preservation upon throwing move construction.
+ flat_multiset source = {1, 2};
+ try
+ {
+ flat_multiset target(std::move(source));
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( source.empty() );
+ }
+
+ // Verify invariant preservation upon throwing move assignment.
+ source.clear();
+ source.insert({1, 2});
+ flat_multiset target = {3, 4};
+ try
+ {
+ target = std::move(source);
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( source.empty() );
+ VERIFY( target.empty() );
+ }
+
+ // Verify invariant preservation upon throwing swap.
+ source.clear();
+ source.insert({1, 2});
+ target.clear();
+ target.insert({3, 4});
+ try
+ {
+ source.swap(target);
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( source.empty() );
+ VERIFY( target.empty() );
+ }
+#endif
+}
+
+constexpr
+void
+test11()
+{
+ // Verify usability of flat_multiset::insert_range(sorted_equivalent_t,
Rg&&).
+ std::flat_multiset<int> m = {2};
+ int s[] = {1, 3};
+ m.insert_range(std::sorted_equivalent, s);
+ VERIFY( std::ranges::equal(m, (int[]){1, 2, 3}) );
+}
+
void
test()
{
@@ -278,6 +368,8 @@ test()
test07();
test08();
test09();
+ test10();
+ test11();
}
constexpr
@@ -292,6 +384,8 @@ test_constexpr()
test07();
test08();
test09();
+ // test10() is non-constexpr
+ test11();
return true;
}
diff --git a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
index a14d03476e18..ac12815bd0d4 100644
--- a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc
@@ -2,7 +2,7 @@
#include <flat_set>
-#if __cpp_lib_flat_set != 202207L
+#if __cpp_lib_flat_set != 202511L
# error "Feature-test macro __cpp_lib_flat_set has wrong value in <flat_set>"
#endif
@@ -277,6 +277,96 @@ test09()
VERIFY( std::ranges::equal(s, (int[]){2,4}) );
}
+template<typename T>
+struct throwing_vector : std::vector<T>
+{
+ static inline bool throw_on_move = false;
+
+ throwing_vector() = default;
+ throwing_vector(const throwing_vector&) = default;
+ throwing_vector& operator=(const throwing_vector&) = default;
+
+ throwing_vector(throwing_vector&& other)
+ : std::vector<T>(std::move(other))
+ {
+ if (throw_on_move)
+ throw std::runtime_error("move ctor");
+ }
+
+ throwing_vector&
+ operator=(throwing_vector&& other)
+ {
+ static_cast<std::vector<T>&>(*this) = std::move(other);
+ if (throw_on_move)
+ throw std::runtime_error("move assign");
+ return *this;
+ }
+};
+
+void
+test10()
+{
+#if __cpp_exceptions
+ using flat_set = std::flat_set<int, std::less<int>, throwing_vector<int>>;
+
+ throwing_vector<int>::throw_on_move = true;
+
+ // Verify invariant preservation upon throwing move construction.
+ flat_set source = {1, 2};
+ try
+ {
+ flat_set target(std::move(source));
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( source.empty() );
+ }
+
+ // Verify invariant preservation upon throwing move assignment.
+ source.clear();
+ source.insert({1, 2});
+ flat_set target = {3, 4};
+ try
+ {
+ target = std::move(source);
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( source.empty() );
+ VERIFY( target.empty() );
+ }
+
+ // Verify invariant preservation upon throwing swap.
+ source.clear();
+ source.insert({1, 2});
+ target.clear();
+ target.insert({3, 4});
+ try
+ {
+ source.swap(target);
+ VERIFY( false );
+ }
+ catch (const std::runtime_error&)
+ {
+ VERIFY( source.empty() );
+ VERIFY( target.empty() );
+ }
+#endif
+}
+
+constexpr
+void
+test11()
+{
+ // Verify usability of flat_set::insert_range(sorted_unique_t, Rg&&).
+ std::flat_set<int> m = {2};
+ int s[] = {1, 3};
+ m.insert_range(std::sorted_unique, s);
+ VERIFY( std::ranges::equal(m, (int[]){1, 2, 3}) );
+}
+
void
test()
{
@@ -290,6 +380,8 @@ test()
test07();
test08();
test09();
+ test10();
+ test11();
}
constexpr
@@ -304,6 +396,8 @@ test_constexpr()
test07();
test08();
test09();
+ // test10() is non-constexpr
+ test11();
return true;
}
--
2.54.0.rc1.54.g60f07c4f5c