Patch reviewed then:

    libstdc++: [_GLIBCXX_DEBUG] Enhance __gnu_debug::inplace_vector implementation

    Review invalidation of iterators to use a RAII approach. A guard type has been     implemented that is in charge of running an invalidation policy on destruction
    when not in a constant evaluation context.

    libstdc++-v3/ChangeLog:

            * include/debug/assertions.h (_GLIBCXX_DEBUG_STD_C): New.
            * include/debug/inplace_vector (__gnu_debug::_Inplace_vector_guard): New.
            (inplace_vector::_InvalidatePolicy): New.
            (inplace_vector::_InvalidateGuard): New.
            (inplace_vector::_InvalidateAfterNthPolicy): New.
            (inplace_vector::_InvalidateAfterNthGuard): New.
[_GLIBCXX_DEBUG_PEDANTIC](inplace_vector::_PedanticInvalidatePolicy): New.
            (inplace_vector::_AssignGuard): New.
            (inplace_vector): Adapt using latter guards.
            * testsuite/23_containers/inplace_vector/debug/initializer_list.cc: New test case.             * testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc:
            New test case.

Tested under Linux x86_64 _GLIBCXX_DEBUG mode.

Ok to commit ?


On 7/8/26 13:26, Jonathan Wakely wrote:
On Tue, 07 Jul 2026 at 14:30 +0100, Jonathan Wakely wrote:
On Mon, 06 Jul 2026 at 18:40 +0200, François Dumont wrote:
    libstdc++: [_GLIBCXX_DEBUG] Enhance __gnu_debug::inplace_vector implementation

    Review invalidation of iterators to use a RAII approach. A guard type has been     implemented that is in charge of running an invalidation policy on destruction
    when not in a constant evaluation context.

Using RAII changes the semantics of these functions when the base
class operation throws an exception. In some cases that's fine.

For resize(n), if it throws then the size doesn't change, and the
_InvalidatePolicy does nothing. Great.

For append_range, if constructing a new element throws, the size
doesn't change, and the guard does nothing. Great.

But for emplace_back, if the new element throws an exception
from its constructor, then the standard says there are no effects. The
size doesn't change, and no iterators are invalidated. But with this
change, the pedantic guard will

I had the same reflection but miss the _InvalidateAllPolicy that require the same _M_active flag that I've done on std::__debug::vector side.



    Invalidation of iterators on assign operation has been reviewed. All iterators     are being invalidated when using the _GLIBCXX_DEBUG_PEDANTIC mode. Otherwise     invalidate only iterators that were removed by the assignment operation.

    libstdc++-v3/ChangeLog:

            * include/debug/assertions.h (_GLIBCXX_DEBUG_STD_C): New.
            * include/debug/inplace_vector (__gnu_debug::_Inplace_vector_guard): New.
            (inplace_vector::_InvalidatePolicy): New.
            (inplace_vector::_InvalidateGuard): New.
            (inplace_vector::_InvalidateAfterNthPolicy): New.
            (inplace_vector::_InvalidateAfterNthGuard): New.
[_GLIBCXX_DEBUG_PEDANTIC](inplace_vector::_PedanticInvalidatePolicy): New.
            (inplace_vector::_AssignGuard): New.
            (inplace_vector): Adapt using latter guards.
            * testsuite/23_containers/inplace_vector/debug/initializer_list.cc: New test case.             * testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc:
            New test case.

Tested under Linux x64_86 _GLIBCXX_DEBUG mode.

Ok to commit ?

François



diff --git a/libstdc++-v3/include/debug/assertions.h b/libstdc++-v3/include/debug/assertions.h
index c4993dc8596..768a7f05228 100644
--- a/libstdc++-v3/include/debug/assertions.h
+++ b/libstdc++-v3/include/debug/assertions.h
@@ -31,6 +31,12 @@

#include <bits/c++config.h>

+#ifdef _GLIBCXX_DEBUG
+# define _GLIBCXX_DEBUG_STD_C ::std::_GLIBCXX_STD_C
+#else
+# define _GLIBCXX_DEBUG_STD_C ::std
+#endif

I've wanted this many times over the years :-)
There are probably other places that we could use this (but not as
part of this patch).

Yes, I've done the same on the std::__debug::vector for now.



#ifndef _GLIBCXX_DEBUG
// Verify that [_First, _Last) forms a non-empty iterator range.
# define __glibcxx_requires_non_empty_range(_First,_Last)    \
diff --git a/libstdc++-v3/include/debug/inplace_vector b/libstdc++-v3/include/debug/inplace_vector
index e579e8636f9..6ce4d006c90 100644
--- a/libstdc++-v3/include/debug/inplace_vector
+++ b/libstdc++-v3/include/debug/inplace_vector
@@ -46,6 +46,33 @@ namespace std _GLIBCXX_VISIBILITY(default) { namespace __debug {
#include <debug/safe_sequence.h>
#include <debug/safe_iterator.h>

+namespace __gnu_debug
+{
+  template<typename _Tp, size_t _Nm, typename _InvalidationPolicy>
+    struct _Inplace_vector_guard
+    {
+      using _SafeInplaceVector = std::__debug::inplace_vector<_Tp, _Nm>; +      using _StdInplaceVector = _GLIBCXX_DEBUG_STD_C::inplace_vector<_Tp, _Nm>; +      using difference_type = typename _StdInplaceVector::difference_type;
+
+      constexpr
+      _Inplace_vector_guard(_SafeInplaceVector& __vector,
+                difference_type __offset = 0)
+      : _M_vector(__vector), _M_invalidation_policy(__vector, __offset) { }
+
+      constexpr
+      ~_Inplace_vector_guard()
+      {
+    if (!std::is_constant_evaluated())
+      _M_invalidation_policy(_M_vector);

This is C++26 code, so should be just:

         if ! consteval {
           _M_invalidation_policy(_M_vector);
         }

This is simpler to compile.
Done.

+      }
+
+    private:
+      _SafeInplaceVector& _M_vector;
+      _InvalidationPolicy _M_invalidation_policy;
+    };
+}
+
namespace std _GLIBCXX_VISIBILITY(default)
{
namespace __debug
@@ -80,6 +107,70 @@ namespace __debug
     using reverse_iterator       = std::reverse_iterator<iterator>;
     using const_reverse_iterator = std::reverse_iterator<const_iterator>;

+    private:
+      struct _InvalidatePolicy
+      {
+    size_type _M_size;
+    _Base_const_iterator _M_end;
+
+    constexpr
+    _InvalidatePolicy(const inplace_vector& __vector,
+                  difference_type)
+    : _M_size(__vector.size()), _M_end(__vector._M_base().end()) { }
+
+    void
+    operator()(inplace_vector& __vector) const noexcept
+    {
+      const size_type __size = __vector.size();
+      if (__size < _M_size)
+        __vector._M_invalidate_after_nth(__size);
+      else if (__size > _M_size)
+        __vector._M_invalidate_if(_Equal(_M_end));
+    }
+      };
+
+      using _InvalidateGuard =
+    __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidatePolicy>;
+
+      struct _InvalidateAfterNthPolicy
+      {
+    size_type _M_size;
+    difference_type _M_offset;
+
+    constexpr
+    _InvalidateAfterNthPolicy(const inplace_vector& __vector,
+                  difference_type __offset)
+    : _M_size(__vector.size()), _M_offset(__offset) { }
+
+    void
+    operator()(inplace_vector& __vector) const noexcept
+    {
+      if (__vector.size() != _M_size)
+        __vector._M_invalidate_after_nth(_M_offset);
+    }
+      };
+
+      using _InvalidateAfterNthGuard =
+    __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidateAfterNthPolicy>;
+
+#ifdef _GLIBCXX_DEBUG_PEDANTIC

Please add comments explaining how and why we're deviating from what
the standard says.

+      struct _PedanticInvalidatePolicy
+      {
+    constexpr
+    _PedanticInvalidatePolicy(const inplace_vector&, difference_type) { }
+
+    void
+    operator()(inplace_vector& __vector) const noexcept
+    { __vector._M_invalidate_all(); }
+      };
+
+      using _AssignGuard =
+    __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _PedanticInvalidatePolicy>;
+#else
+      using _AssignGuard = _InvalidateGuard;
+#endif
+
+    public:
     constexpr
     inplace_vector() noexcept = default;

@@ -120,8 +211,8 @@ namespace __debug
     constexpr inplace_vector&
     operator=(initializer_list<_Tp> __il)
     {
+    _AssignGuard __guard(*this);
    _Base::operator=(__il);
-    this->_M_invalidate_all();
    return *this;
     }

@@ -129,65 +220,63 @@ namespace __debug
    constexpr void
    assign(_InputIterator __first, _InputIterator __last)
    {
+      if (std::is_constant_evaluated())

Use 'if consteval' here.

+        return _Base::assign(__gnu_debug::__unsafe(__first),
+                 __gnu_debug::__unsafe(__last));
+
      typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
      __glibcxx_check_valid_range2(__first, __last, __dist);

-      const auto __size = size();
-      const auto __end = _Base::end();
+      _AssignGuard __guard(*this);
      if (__dist.second >= __gnu_debug::__dp_sign)
        _Base::assign(__gnu_debug::__unsafe(__first),
              __gnu_debug::__unsafe(__last));
      else
        _Base::assign(__first, __last);
-
-      if (size() < __size)
-        _M_invalidate_after_nth(size());
-      else if (size() > __size)
-        this->_M_invalidate_if(_Equal(__end));
    }

template<__detail::__container_compatible_range<_Tp> _Rg>
    constexpr void
    assign_range(_Rg&& __rg)
    {
+      _AssignGuard __guard(*this);
      _Base::assign_range(std::forward<_Rg>(__rg));
-      this->_M_invalidate_all();
    }

     constexpr void
     assign(size_type __n, const _Tp& __u)
     {
+    _AssignGuard __guard(*this);
    _Base::assign(__n, __u);
-    this->_M_invalidate_all();
     }

     constexpr void
     assign(initializer_list<_Tp> __il)
     {
+    _AssignGuard __guard(*this);
    _Base::assign(__il);
-    this->_M_invalidate_all();
     }

     // iterators
     [[nodiscard]]
     constexpr iterator
     begin() noexcept
-      { return { _Base::begin(), this }; }
+      { return iterator(_Base::begin(), this); }

     [[nodiscard]]
     constexpr const_iterator
     begin() const noexcept
-      { return { _Base::begin(), this }; }
+      { return const_iterator(_Base::begin(), this); }

     [[nodiscard]]
     constexpr iterator
     end() noexcept
-      { return { _Base::end(), this }; }
+      { return iterator(_Base::end(), this); }

     [[nodiscard]]
     constexpr const_iterator
     end() const noexcept
-      { return { _Base::end(), this }; }
+      { return const_iterator(_Base::end(), this); }

     [[nodiscard]]
     constexpr reverse_iterator
@@ -210,12 +299,12 @@ namespace __debug
     [[nodiscard]]
     constexpr const_iterator
     cbegin() const noexcept
-      { return { _Base::cbegin(), this }; }
+      { return const_iterator(_Base::cbegin(), this); }

     [[nodiscard]]
     constexpr const_iterator
     cend() const noexcept
-      { return { _Base::cend(), this }; }
+      { return const_iterator(_Base::cend(), this); }

     [[nodiscard]]
     constexpr const_reverse_iterator
@@ -233,15 +322,15 @@ namespace __debug
     constexpr void
     resize(size_type __n)
     {
+    _InvalidateGuard __guard(*this);
    _Base::resize(__n);
-    _M_invalidate_after_nth(__n);
     }

     constexpr void
     resize(size_type __n, const _Tp& __c)
     {
+    _InvalidateGuard __guard(*this);
    _Base::resize(__n, __c);
-    _M_invalidate_after_nth(__n);
     }

     using _Base::reserve;
@@ -304,10 +393,8 @@ namespace __debug
    constexpr _Tp&
    emplace_back(_Args&&... __args)
    {
-      const auto __end = _Base::cend();
-      _Tp& __res = _Base::emplace_back(std::forward<_Args>(__args)...);
-      this->_M_invalidate_if(_Equal(__end));
-      return __res;
+      _InvalidateGuard __guard(*this);
+      return _Base::emplace_back(std::forward<_Args>(__args)...);
    }

     constexpr _Tp&
@@ -322,18 +409,15 @@ namespace __debug
    constexpr void
    append_range(_Rg&& __rg)
    {
-      const auto __size = size();
-      const auto __end = _Base::cend();
+      _InvalidateGuard __guard(*this);
      _Base::append_range(__rg);
-      if (size() != __size)
-        this->_M_invalidate_if(_Equal(__end));
    }

     constexpr void
     pop_back()
     {
    __glibcxx_check_nonempty();
-    _M_invalidate_after_nth(_Base::size() - 1);
+    _InvalidateGuard __guard(*this);
    _Base::pop_back();
     }

@@ -341,51 +425,30 @@ namespace __debug
    constexpr optional<_Tp&>
    try_emplace_back(_Args&&... __args)
    {
-      auto __end = _Base::cend();
-      optional<_Tp&> __res
-        = _Base::try_emplace_back(std::forward<_Args>(__args)...);
-
-      if (__res)
-        this->_M_invalidate_if(_Equal(__end));
-
-      return __res;
+      _InvalidateGuard __guard(*this);
+      return _Base::try_emplace_back(std::forward<_Args>(__args)...);
    }

     constexpr optional<_Tp&>
     try_push_back(const _Tp& __x)
     {
-    const auto __end = _Base::cend();
-    optional<_Tp&> __res = _Base::try_push_back(__x);
-
-    if (__res)
-      this->_M_invalidate_if(_Equal(__end));
-
-    return __res;
+    _InvalidateGuard __guard(*this);
+    return _Base::try_push_back(__x);
     }

     constexpr optional<_Tp&>
     try_push_back(_Tp&& __x)
     {
-    const auto __end = _Base::cend();
-    optional<_Tp&> __res = _Base::try_push_back(std::move(__x));
-
-    if (__res)
-      this->_M_invalidate_if(_Equal(__end));
-
-    return __res;
+    _InvalidateGuard __guard(*this);
+    return _Base::try_push_back(std::move(__x));
     }

     template<typename... _Args>
    constexpr _Tp&
    unchecked_emplace_back(_Args&&... __args)
    {

I think the following comment would be useful here:

         // Precondition is checked in _Base::unchecked_emplace_back

Because I was just thinking that the debug inplace_vector should check
the precondition even though this is the "unchecked" API. But it is
already checked with __glibcxx_assert in the base.

Done even if there is still the problem of direct use of __gnu_debug::inplace_vector without activating _GLIBCXX_DEBUG nor _GLIBCXX_ASSERTIONS.

I'll deal with that in a future patch because I would need a functional __glibcxx_assert without _GLIBCXX_ASSERTIONS in this case.


-      const auto __end = _Base::cend();
-      _Tp& __res =
- _Base::unchecked_emplace_back(std::forward<_Args>(__args)...);
-
-      this->_M_invalidate_if(_Equal(__end));
-
-      return __res;
+      _InvalidateGuard __guard(*this);
+      return _Base::unchecked_emplace_back(std::forward<_Args>(__args)...);
    }

     constexpr _Tp&
@@ -407,9 +470,12 @@ namespace __debug

      __glibcxx_check_insert(__position);
      const difference_type __offset = __position.base() - _Base::cbegin();
-      _Base_iterator __res = _Base::emplace(__position.base(),
- std::forward<_Args>(__args)...);
-      _M_invalidate_after_nth(__offset);
+      _Base_iterator __res;
+      {

Why is the extra block scope needed here?

Ah, because otherwise we invalidate the return value as soon as we
construct it? I think that deserves a comment. Arguably, the code was
simpler before. Using the RAII guard take extra lines, and has the
subtle dependency on a new scope. But it does guarantee we invalidate
even if the _Base::emplace call throws.

Comment added, as well as in the std::__debug::vector patch.



What if we just called _M_invalidate_after_nth(__offset) before
calling _Base::emplace? Do we lose anything that way?

We wouldn't lose anything. I would just prefer to keep a consistent RAII approach. As this code is not compiled in case of constant-evaluation I think there is no drawback for the few extra code, no ?



It's possible that one of the arguments to emplace could be an
iterator into the vector (or something that contains such an iterator)
and invalidating it would break construction of the new element. But I
think that case is undefined anyway, accessing an element of the
vector while emplacing a new element into it violates the reentrancy
rules.

+        _InvalidateAfterNthGuard __guard(*this, __offset);
+        __res = _Base::emplace(__position.base(),
+                   std::forward<_Args>(__args)...);
+      }
      return { __res, this };

Should the line above be changed to return iterator(__res, this) as
done elsewhere by this patch?

Alternatively, don't change any of the return {...} statements and
leave them using braces (is there a reason to change them?)

AFAIK, through my different attempts to deal with constant evaluation, the value initialization is not constant evaluation compatible. This is why I've changed some iterator instantiations to use the proper constructor instead.

But here this code is not evaluated during constant evaluation so I preserved it.

François

diff --git a/libstdc++-v3/include/debug/assertions.h 
b/libstdc++-v3/include/debug/assertions.h
index c4993dc8596..768a7f05228 100644
--- a/libstdc++-v3/include/debug/assertions.h
+++ b/libstdc++-v3/include/debug/assertions.h
@@ -31,6 +31,12 @@
 
 #include <bits/c++config.h>
 
+#ifdef _GLIBCXX_DEBUG
+# define _GLIBCXX_DEBUG_STD_C ::std::_GLIBCXX_STD_C
+#else
+# define _GLIBCXX_DEBUG_STD_C ::std
+#endif
+
 #ifndef _GLIBCXX_DEBUG
 // Verify that [_First, _Last) forms a non-empty iterator range.
 # define __glibcxx_requires_non_empty_range(_First,_Last)      \
diff --git a/libstdc++-v3/include/debug/inplace_vector 
b/libstdc++-v3/include/debug/inplace_vector
index e579e8636f9..ba079bca171 100644
--- a/libstdc++-v3/include/debug/inplace_vector
+++ b/libstdc++-v3/include/debug/inplace_vector
@@ -46,6 +46,49 @@ namespace std _GLIBCXX_VISIBILITY(default) { namespace 
__debug {
 #include <debug/safe_sequence.h>
 #include <debug/safe_iterator.h>
 
+namespace __gnu_debug
+{
+  template<typename _Tp, size_t _Nm, typename _InvalidationPolicy>
+    class _Inplace_vector_guard
+    {
+      using _SafeInplaceVector = std::__debug::inplace_vector<_Tp, _Nm>;
+      using _StdInplaceVector = _GLIBCXX_DEBUG_STD_C::inplace_vector<_Tp, _Nm>;
+      using difference_type = typename _StdInplaceVector::difference_type;
+
+      constexpr
+      _Inplace_vector_guard(_SafeInplaceVector& __vector,
+                           difference_type __offset, bool __active)
+      : _M_vector(__vector), _M_invalidation_policy(__vector, __offset)
+      , _M_active(__active) { }
+
+      _SafeInplaceVector& _M_vector;
+      _InvalidationPolicy _M_invalidation_policy;
+
+    protected:
+      constexpr
+      _Inplace_vector_guard(_SafeInplaceVector& __vector, bool __active)
+      : _Inplace_vector_guard(__vector, 0, __active) { }
+
+    public:
+      bool _M_active;
+
+      constexpr
+      _Inplace_vector_guard(_SafeInplaceVector& __vector,
+                           difference_type __offset = 0)
+      : _Inplace_vector_guard(__vector, __offset, true) { }
+
+      constexpr
+      ~_Inplace_vector_guard()
+      {
+       if ! consteval
+         {
+           if (_M_active)
+             _M_invalidation_policy(_M_vector);
+         }
+      }
+    };
+}
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 namespace __debug
@@ -80,6 +123,71 @@ namespace __debug
       using reverse_iterator       = std::reverse_iterator<iterator>;
       using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
+    private:
+      struct _InvalidatePolicy
+      {
+       size_type _M_size;
+       _Base_const_iterator _M_end;
+
+       constexpr
+       _InvalidatePolicy(const inplace_vector& __vector, difference_type)
+       : _M_size(__vector.size()), _M_end(__vector._M_base().end()) { }
+
+       void
+       operator()(inplace_vector& __vector) const noexcept
+       {
+         const size_type __size = __vector.size();
+         if (__size < _M_size)
+           __vector._M_invalidate_after_nth(__size);
+         else if (__size > _M_size)
+           __vector._M_invalidate_if(_Equal(_M_end));
+       }
+      };
+
+      using _InvalidateGuard =
+       __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidatePolicy>;
+
+      struct _InvalidateAfterNthPolicy
+      {
+       size_type _M_size;
+       difference_type _M_offset;
+
+       constexpr
+       _InvalidateAfterNthPolicy(const inplace_vector& __vector,
+                                 difference_type __offset)
+       : _M_size(__vector.size()), _M_offset(__offset) { }
+
+       void
+       operator()(inplace_vector& __vector) const noexcept
+       {
+         if (__vector.size() != _M_size)
+           __vector._M_invalidate_after_nth(_M_offset);
+       }
+      };
+
+      using _InvalidateAfterNthGuard =
+       __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidateAfterNthPolicy>;
+
+      struct _InvalidateAllPolicy
+      {
+       constexpr
+       _InvalidateAllPolicy(const inplace_vector&, difference_type) { }
+
+       void
+       operator()(inplace_vector& __vector) const noexcept
+       { __vector._M_invalidate_all(); }
+      };
+
+      struct _InvalidateAllGuard
+       : __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidateAllPolicy>
+      {
+       constexpr
+       _InvalidateAllGuard(inplace_vector& __vector)
+       : __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidateAllPolicy>
+         (__vector, false) { }
+      };
+
+    public:
       constexpr
       inplace_vector() noexcept = default;
 
@@ -120,8 +228,9 @@ namespace __debug
       constexpr inplace_vector&
       operator=(initializer_list<_Tp> __il)
       {
+       _InvalidateAllGuard __guard(*this);
        _Base::operator=(__il);
-       this->_M_invalidate_all();
+       __guard._M_active = true;
        return *this;
       }
 
@@ -129,65 +238,69 @@ namespace __debug
        constexpr void
        assign(_InputIterator __first, _InputIterator __last)
        {
+         if consteval
+           {
+             return _Base::assign(__gnu_debug::__unsafe(__first),
+                                  __gnu_debug::__unsafe(__last));
+           }
+
          typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
          __glibcxx_check_valid_range2(__first, __last, __dist);
 
-         const auto __size = size();
-         const auto __end = _Base::end();
+         _InvalidateAllGuard __guard(*this);
          if (__dist.second >= __gnu_debug::__dp_sign)
            _Base::assign(__gnu_debug::__unsafe(__first),
                          __gnu_debug::__unsafe(__last));
          else
            _Base::assign(__first, __last);
-
-         if (size() < __size)
-           _M_invalidate_after_nth(size());
-         else if (size() > __size)
-           this->_M_invalidate_if(_Equal(__end));
+         __guard._M_active = true;
        }
 
       template<__detail::__container_compatible_range<_Tp> _Rg>
        constexpr void
        assign_range(_Rg&& __rg)
        {
+         _InvalidateAllGuard __guard(*this);
          _Base::assign_range(std::forward<_Rg>(__rg));
-         this->_M_invalidate_all();
+         __guard._M_active = true;
        }
 
       constexpr void
       assign(size_type __n, const _Tp& __u)
       {
+       _InvalidateAllGuard __guard(*this);
        _Base::assign(__n, __u);
-       this->_M_invalidate_all();
+       __guard._M_active = true;
       }
 
       constexpr void
       assign(initializer_list<_Tp> __il)
       {
+       _InvalidateAllGuard __guard(*this);
        _Base::assign(__il);
-       this->_M_invalidate_all();
+       __guard._M_active = true;
       }
 
       // iterators
       [[nodiscard]]
       constexpr iterator
       begin() noexcept
-      { return { _Base::begin(), this }; }
+      { return iterator(_Base::begin(), this); }
 
       [[nodiscard]]
       constexpr const_iterator
       begin() const noexcept
-      { return { _Base::begin(), this }; }
+      { return const_iterator(_Base::begin(), this); }
 
       [[nodiscard]]
       constexpr iterator
       end() noexcept
-      { return { _Base::end(), this }; }
+      { return iterator(_Base::end(), this); }
 
       [[nodiscard]]
       constexpr const_iterator
       end() const noexcept
-      { return { _Base::end(), this }; }
+      { return const_iterator(_Base::end(), this); }
 
       [[nodiscard]]
       constexpr reverse_iterator
@@ -210,12 +323,12 @@ namespace __debug
       [[nodiscard]]
       constexpr const_iterator
       cbegin() const noexcept
-      { return { _Base::cbegin(), this }; }
+      { return const_iterator(_Base::cbegin(), this); }
 
       [[nodiscard]]
       constexpr const_iterator
       cend() const noexcept
-      { return { _Base::cend(), this }; }
+      { return const_iterator(_Base::cend(), this); }
 
       [[nodiscard]]
       constexpr const_reverse_iterator
@@ -233,15 +346,15 @@ namespace __debug
       constexpr void
       resize(size_type __n)
       {
+       _InvalidateGuard __guard(*this);
        _Base::resize(__n);
-       _M_invalidate_after_nth(__n);
       }
 
       constexpr void
       resize(size_type __n, const _Tp& __c)
       {
+       _InvalidateGuard __guard(*this);
        _Base::resize(__n, __c);
-       _M_invalidate_after_nth(__n);
       }
 
       using _Base::reserve;
@@ -304,10 +417,8 @@ namespace __debug
        constexpr _Tp&
        emplace_back(_Args&&... __args)
        {
-         const auto __end = _Base::cend();
-         _Tp& __res =  _Base::emplace_back(std::forward<_Args>(__args)...);
-         this->_M_invalidate_if(_Equal(__end));
-         return __res;
+         _InvalidateGuard __guard(*this);
+         return _Base::emplace_back(std::forward<_Args>(__args)...);
        }
 
       constexpr _Tp&
@@ -322,18 +433,15 @@ namespace __debug
        constexpr void
        append_range(_Rg&& __rg)
        {
-         const auto __size = size();
-         const auto __end = _Base::cend();
+         _InvalidateGuard __guard(*this);
          _Base::append_range(__rg);
-         if (size() != __size)
-           this->_M_invalidate_if(_Equal(__end));
        }
 
       constexpr void
       pop_back()
       {
        __glibcxx_check_nonempty();
-       _M_invalidate_after_nth(_Base::size() - 1);
+       _InvalidateGuard __guard(*this);
        _Base::pop_back();
       }
 
@@ -341,51 +449,31 @@ namespace __debug
        constexpr optional<_Tp&>
        try_emplace_back(_Args&&... __args)
        {
-         auto __end = _Base::cend();
-         optional<_Tp&> __res
-           = _Base::try_emplace_back(std::forward<_Args>(__args)...);
-
-         if (__res)
-           this->_M_invalidate_if(_Equal(__end));
-
-         return __res;
+         _InvalidateGuard __guard(*this);
+         return _Base::try_emplace_back(std::forward<_Args>(__args)...);
        }
 
       constexpr optional<_Tp&>
       try_push_back(const _Tp& __x)
       {
-       const auto __end = _Base::cend();
-       optional<_Tp&> __res = _Base::try_push_back(__x);
-
-       if (__res)
-         this->_M_invalidate_if(_Equal(__end));
-
-       return __res;
+       _InvalidateGuard __guard(*this);
+       return _Base::try_push_back(__x);
       }
 
       constexpr optional<_Tp&>
       try_push_back(_Tp&& __x)
       {
-       const auto __end = _Base::cend();
-       optional<_Tp&> __res = _Base::try_push_back(std::move(__x));
-
-       if (__res)
-         this->_M_invalidate_if(_Equal(__end));
-
-       return __res;
+       _InvalidateGuard __guard(*this);
+       return _Base::try_push_back(std::move(__x));
       }
 
       template<typename... _Args>
        constexpr _Tp&
        unchecked_emplace_back(_Args&&... __args)
        {
-         const auto __end = _Base::cend();
-         _Tp& __res =
-           _Base::unchecked_emplace_back(std::forward<_Args>(__args)...);
-
-         this->_M_invalidate_if(_Equal(__end));
-
-         return __res;
+         // Precondition is checked in _Base::unchecked_emplace_back
+         _InvalidateGuard __guard(*this);
+         return _Base::unchecked_emplace_back(std::forward<_Args>(__args)...);
        }
 
       constexpr _Tp&
@@ -400,16 +488,22 @@ namespace __debug
        constexpr iterator
        emplace(const_iterator __position, _Args&&... __args)
        {
-         if (std::is_constant_evaluated())
+         if consteval
+           {
              return iterator(_Base::emplace(__position.base(),
                                             std::forward<_Args>(__args)...),
                              this);
+           }
 
          __glibcxx_check_insert(__position);
          const difference_type __offset = __position.base() - _Base::cbegin();
-         _Base_iterator __res = _Base::emplace(__position.base(),
+         _Base_iterator __res;
+         {
+           // Scoped guard to avoid invalidation of the returned iterator.
+           _InvalidateAfterNthGuard __guard(*this, __offset);
+           __res = _Base::emplace(__position.base(),
                                   std::forward<_Args>(__args)...);
-         _M_invalidate_after_nth(__offset);
+         }
          return { __res, this };
        }
 
@@ -424,13 +518,19 @@ namespace __debug
       constexpr iterator
       insert(const_iterator __position, size_type __n, const _Tp& __x)
       {
-       if (std::is_constant_evaluated())
+       if consteval
+         {
            return iterator(_Base::insert(__position.base(), __n, __x), this);
+         }
 
        __glibcxx_check_insert(__position);
        const difference_type __offset = __position.base() - _Base::cbegin();
-       _Base_iterator __res = _Base::insert(__position.base(), __n, __x);
-       _M_invalidate_after_nth(__offset);
+       _Base_iterator __res;
+       {
+         // Scoped guard to avoid invalidation of the returned iterator.
+         _InvalidateAfterNthGuard __guard(*this, __offset);
+         __res = _Base::insert(__position.base(), __n, __x);
+       }
        return { __res, this };
       }
 
@@ -439,24 +539,30 @@ namespace __debug
        insert(const_iterator __position, _InputIterator __first,
               _InputIterator __last)
        {
-         if (std::is_constant_evaluated())
+         if consteval
+           {
              return iterator(_Base::insert(__position.base(),
                                            __gnu_debug::__unsafe(__first),
-                                         __gnu_debug::__unsafe(__last)), this);
+                                           __gnu_debug::__unsafe(__last)),
+                             this);
+           }
 
          typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
          __glibcxx_check_insert_range(__position, __first, __last, __dist);
 
          const difference_type __offset = __position.base() - _Base::cbegin();
          _Base_iterator __res;
+         {
+           // Scoped guard to avoid invalidation of the returned iterator.
+           _InvalidateAfterNthGuard __guard(*this, __offset);
            if (__dist.second >= __gnu_debug::__dp_sign)
              __res = _Base::insert(__position.base(),
                                    __gnu_debug::__unsafe(__first),
                                    __gnu_debug::__unsafe(__last));
            else
              __res = _Base::insert(__position.base(), __first, __last);
+         }
 
-         _M_invalidate_after_nth(__offset);
          return { __res, this };
        }
 
@@ -464,11 +570,13 @@ namespace __debug
        constexpr iterator
        insert_range(const_iterator __position, _Rg&& __rg)
        {
-         const auto __size = size();
          const difference_type __offset = __position.base() - _Base::cbegin();
-         auto __res = _Base::insert_range(__position.base(), __rg);
-         if (size() > __size)
-           this->_M_invalidate_after_nth(__offset);
+         _Base_iterator __res;
+         {
+           // Scoped guard to avoid invalidation of the returned iterator.
+           _InvalidateAfterNthGuard __guard(*this, __offset);
+           __res = _Base::insert_range(__position.base(), __rg);
+         }
 
          return iterator(__res, this);
        }
@@ -476,46 +584,61 @@ namespace __debug
       constexpr iterator
       insert(const_iterator __position, initializer_list<_Tp> __il)
       {
-       if (std::is_constant_evaluated())
+       if consteval
+         {
            return iterator(_Base::insert(__position.base(), __il), this);
+         }
 
        __glibcxx_check_insert(__position);
-       const auto __size = size();
        difference_type __offset = __position.base() - _Base::begin();
-       _Base_iterator __res = _Base::insert(__position.base(), __il);
-       if (size() > __size)
-         this->_M_invalidate_after_nth(__offset);
+       _Base_iterator __res;
+       {
+         // Scoped guard to avoid invalidation of the returned iterator.
+         _InvalidateAfterNthGuard __guard(*this, __offset);
+         __res = _Base::insert(__position.base(), __il);
+       }
+
        return iterator(__res, this);
       }
 
       constexpr iterator
       erase(const_iterator __position)
       {
-       if (std::is_constant_evaluated())
+       if consteval
+         {
            return iterator(_Base::erase(__position.base()), this);
+         }
 
        __glibcxx_check_erase(__position);
        difference_type __offset = __position.base() - _Base::cbegin();
-       _Base_iterator __res = _Base::erase(__position.base());
-       this->_M_invalidate_after_nth(__offset);
+       _Base_iterator __res;
+       {
+         // Scoped guard to avoid invalidation of the returned iterator.
+         _InvalidateAfterNthGuard __guard(*this, __offset);
+         __res = _Base::erase(__position.base());
+       }
        return iterator(__res, this);
       }
 
       constexpr iterator
       erase(const_iterator __first, const_iterator __last)
       {
-       if (std::is_constant_evaluated())
+       if consteval
+         {
            return iterator(_Base::erase(__first.base(), __last.base()),
                            this);
+         }
 
        __glibcxx_check_erase_range(__first, __last);
-
        if (__first.base() != __last.base())
          {
            difference_type __offset = __first.base() - _Base::cbegin();
-           _Base_iterator __res = _Base::erase(__first.base(),
-                                               __last.base());
-           this->_M_invalidate_after_nth(__offset);
+           _Base_iterator __res;
+           {
+             // Scoped guard to avoid invalidation of the returned iterator.
+             _InvalidateAfterNthGuard __guard(*this, __offset);
+             __res = _Base::erase(__first.base(), __last.base());
+           }
            return { __res, this };
          }
        else
diff --git 
a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc
new file mode 100644
index 00000000000..404a565e84d
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc
@@ -0,0 +1,26 @@
+// { dg-do run { target c++26 } }
+
+#include <debug/inplace_vector>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  __gnu_debug::inplace_vector<int, 6> v = { 0, 1, 2, 3, 4, 5 };
+
+  auto it1 = v.begin() + 1;
+  auto it4 = v.begin() + 4;
+
+  VERIFY( !it1._M_singular() );
+  VERIFY( !it4._M_singular() );
+
+  v = { 10, 11, 12, 13 };
+
+  VERIFY( it1._M_singular() );
+  VERIFY( it4._M_singular() );
+}
+
+int main()
+{
+  test01();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list_exception.cc
 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list_exception.cc
new file mode 100644
index 00000000000..bb73cc4822d
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list_exception.cc
@@ -0,0 +1,50 @@
+// { dg-do run { target c++26 } }
+
+#include <debug/inplace_vector>
+#include <testsuite_hooks.h>
+
+struct throwing_type
+{
+  static bool do_throw;
+
+  throwing_type&
+  operator=(const throwing_type&)
+  {
+    if (do_throw)
+      throw "exception";
+    return *this;
+  }
+};
+
+bool throwing_type::do_throw = false;
+
+void
+test01()
+{
+  __gnu_debug::inplace_vector<throwing_type, 6> v(6);
+
+  auto it1 = v.begin() + 1;
+  auto it4 = v.begin() + 4;
+
+  VERIFY( !it1._M_singular() );
+  VERIFY( !it4._M_singular() );
+
+  throwing_type::do_throw = true;
+  try
+    {
+      v = { throwing_type{}, throwing_type{} };
+      VERIFY( false );
+    }
+  catch (const char*)
+    {
+      VERIFY( true );
+    }
+
+  VERIFY( !it1._M_singular() );
+  VERIFY( !it4._M_singular() );
+}
+
+int main()
+{
+  test01();
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/1.cc 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/1.cc
index 1a852739de0..ca630d75b1f 100644
--- 
a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/1.cc
+++ 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/1.cc
@@ -19,7 +19,7 @@ void test01()
 
   i = v1.end();
   v1.assign(v2.begin(), v2.end());
-  VERIFY( !i._M_singular() );
+  VERIFY( i._M_singular() );
 
   i = v1.end();
   v1.assign(17, 42);

Reply via email to