This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4629. inplace_vector insertion functions do not specify iterator invalidation

Section: 23.3.16.5 [inplace.vector.modifiers] Status: New Submitter: Karolis Špukas Opened: 2026-10-06 Last modified: 2026-10-10

Priority: Not Prioritized

View all other issues in [inplace.vector.modifiers].

View all issues with New status.

Discussion:

23.3.16.5 [inplace.vector.modifiers] does not specify iterator and reference invalidation for insertion functions.

According to 23.2.2.2 [container.reqmts] p67, the current wording of 23.3.16.5 [inplace.vector.modifiers] implies that inplace_vector insertions do not invalidate iterators and references or change the values of objects in the container:

Unless otherwise specified (either explicitly or by defining a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container.

P0843R8 specified reference invalidation for insert and emplace, for example:

constexpr iterator insert(const_iterator position, value_type&& x);

Effects: Inserts x at position and invalidates all references to elements after position.

This wording was removed in P0843R9 with no corresponding changelog entry.

On the other hand, 23.3.16.5 [inplace.vector.modifiers] p19 still specifies invalidation for erasure:

Effects: Invalidates iterators and references at or after the point of the erase.

For comparison, 23.3.13.5 [vector.modifiers]/2 specifies invalidation for insertion without reallocation:

If no reallocation happens, then references, pointers, and iterators before the insertion point remain valid but those at or after the insertion point, including the past-the-end iterator, are invalidated.

I took a quick look at libstdc++ implementation. I believe an insertion to inplace_vector can shift elements, therefore it is inconsistent with 23.2.2.2 [container.reqmts] p67.

Proposed resolution:

This wording is relative to N5054.

  1. Modify 23.3.16.5 [inplace.vector.modifiers] as indicated:

    constexpr iterator insert(const_iterator position, const T& x);
    constexpr iterator insert(const_iterator position, T&& x);
    […]
    template<class... Args>
      constexpr iterator emplace(const_iterator position, Args&&... args);
    template<container-compatible-range <T> R>
      constexpr void append_range(R&& rg);
    

    -1- Let n be the value of size() before this call for the append_range overload, and `distance(begin, position)` otherwise.

    -2- Complexity: Linear in the number of elements inserted plus the distance to the end of the vector.

    -3- Remarks: References, pointers, and iterators before the insertion point remain valid, but those at or after the insertion point, including the past-the-end iterator, are invalidated. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation, there are no effects. Otherwise, if an exception is thrown, then size() ≥ n and elements in the range begin() + [0, n) are not modified.

    constexpr reference push_back(const T& x);
    constexpr reference push_back(T&& x);
    template<class... Args>
      constexpr reference emplace_back(Args&&... args);
    

    -4- Returns: back().

    -5- Throws: bad_alloc or any exception thrown by the initialization of the inserted element.

    -6- Complexity: Constant.

    -7- Remarks: If an element is appended, invalidates the past-the-end iterator. If an exception is thrown, there are no effects on *this.

    template<class... Args>
      constexpr optional<reference> try_emplace_back(Args&&... args);
    constexpr optional<reference> try_push_back(const T& x);
    constexpr optional<reference> try_push_back(T&& x);
    

    -8- Let vals denote a pack: […]

    -9- Preconditions: […]

    -10- Effects: If size() < capacity() is true, appends an object of type T direct-non-list-initialized with vals.... Otherwise, there are no effects.

    -11- Returns: nullopt if size() == capacity() is true, otherwise optional<reference>(in_place, back()).

    -12- Throws: Nothing unless an exception is thrown by the initialization of the inserted element.

    -13- Complexity: Constant.

    -14- Remarks: If an element is appended, invalidates the past-the-end iterator. If an exception is thrown, there are no effects on *this.