On Thu, 28 May 2026 at 15:01, Patrick Palka <[email protected]> wrote:
>
> 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.

Yeah, that seems much simpler.

> >
> > >
> > > 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?

v4 (with the assertion changed) is OK for both - thanks

> >
> > -- >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

Reply via email to