Skip to content

<vector>: vector<bool>::insert(where, first, last) has forbidden complexity for input iterators #3007

@StephanTLavavej

Description

@StephanTLavavej

STL/stl/inc/vector

Lines 3147 to 3149 in 2263d93

for (; _First != _Last; ++_First, (void) ++_Off) {
insert(begin() + _Off, *_First);
}

When we're given input-only iterators, this repeatedly mid-inserts, shifting down existing elements to make room. That's O((last - first) * (vb.end() - where)), which is forbidden by WG21-N4910 [vector.modifiers]/1:

Complexity: If reallocation happens, linear in the number of elements of the resulting vector; otherwise, linear in the number of elements inserted plus the distance to the end of the vector.

Which applies to vector<bool> too, [vector.bool]/2:

Unless described below, all operations have the same requirements and semantics as the primary vector template, except that operations dealing with the bool value type map to bit values in the container storage and allocator_traits::construct (20.2.8.3) is not used to construct these values.

It's extra horrible because each insertion is performing a ton of bitwise work to shift the packed elements down by 1 bit.

Will also affect vector<bool>::insert_range(where, range) after merging #2806.


Note that vector<T> is unaffected because it appends followed by rotating the elements into place:

STL/stl/inc/vector

Lines 1046 to 1070 in 2263d93

template <class _Iter>
_CONSTEXPR20 void _Insert_input_range(const_iterator _Where, _Iter _First, _Iter _Last) {
// insert input range [_First, _Last) at _Where
if (_First == _Last) {
return; // nothing to do, avoid invalidating iterators
}
auto& _My_data = _Mypair._Myval2;
pointer& _Myfirst = _My_data._Myfirst;
pointer& _Mylast = _My_data._Mylast;
const auto _Whereoff = static_cast<size_type>(_Where._Ptr - _Myfirst);
const auto _Oldsize = static_cast<size_type>(_Mylast - _Myfirst);
// For one-at-back, provide strong guarantee.
// Otherwise, provide basic guarantee (despite N4659 26.3.11.5 [vector.modifiers]/1).
// Performance note: except for one-at-back, _Emplace_one_at_back()'s strong guarantee is unnecessary here.
for (; _First != _Last; ++_First) {
_Emplace_one_at_back(*_First);
}
_Orphan_range(_Myfirst + _Whereoff, _Myfirst + _Oldsize);
_STD rotate(_Myfirst + _Whereoff, _Myfirst + _Oldsize, _Mylast);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixedSomething works now, yay!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions