This patch introduces a new function, _M_fill_append, which is invoked when copies of the same value are appended to the end of a vector. Unlike _M_fill_insert(end(), n, v), _M_fill_append never permute elements in place, so it does not require: * vector element type to be assignable; * a copy of the inserted value, in the case where it points to an element of the vector.
vector::resize(n, v) now uses _M_fill_append, fixing the non-conformance where element types were required to be assignable. In addition, _M_fill_insert(end(), n, v) now delegates to _M_fill_append, which eliminates an unnecessary copy of v when the existing capacity is used. PR libstdc++/90192 libstdc++-v3/ChangeLog: * include/bits/stl_vector.h (vector<T>::_M_fill_append): Declare. (vector<T>::fill): Use _M_fill_append instead of _M_fill_insert. * include/bits/vector.tcc (vector<T>::_M_fill_append): Define (vector<T>::_M_fill_insert): Delegate to _M_fill_append when elements are appended. * testsuite/23_containers/vector/modifiers/moveable.cc: Updated copycount for inserting at the end (appending). * testsuite/23_containers/vector/modifiers/resize.cc: New test. --- Tested x86_64-linux locally. The modifiers/resize.cc tested also with c++98/11/14/17/20/23/26 and -DDEBUG. OK for trunk? libstdc++-v3/include/bits/stl_vector.h | 9 ++- libstdc++-v3/include/bits/vector.tcc | 60 +++++++++++++++- .../vector/modifiers/moveable.cc | 6 +- .../23_containers/vector/modifiers/resize.cc | 69 +++++++++++++++++++ 4 files changed, 138 insertions(+), 6 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/vector/modifiers/resize.cc diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h index f2c1bce1e38..7625333c9ad 100644 --- a/libstdc++-v3/include/bits/stl_vector.h +++ b/libstdc++-v3/include/bits/stl_vector.h @@ -1154,7 +1154,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER resize(size_type __new_size, const value_type& __x) { if (__new_size > size()) - _M_fill_insert(end(), __new_size - size(), __x); + _M_fill_append(__new_size - size(), __x); else if (__new_size < size()) _M_erase_at_end(this->_M_impl._M_start + __new_size); } @@ -1175,7 +1175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER resize(size_type __new_size, value_type __x = value_type()) { if (__new_size > size()) - _M_fill_insert(end(), __new_size - size(), __x); + _M_fill_append(__new_size - size(), __x); else if (__new_size < size()) _M_erase_at_end(this->_M_impl._M_start + __new_size); } @@ -2088,6 +2088,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER void _M_fill_insert(iterator __pos, size_type __n, const value_type& __x); + // Called by resize(n,x), and the _M_fill_insert(end(), n, x) + _GLIBCXX20_CONSTEXPR + void + _M_fill_append(size_type __n, const value_type& __x); + #if __cplusplus >= 201103L // Called by resize(n). _GLIBCXX20_CONSTEXPR diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc index dd3d3c6fd65..58cc472b968 100644 --- a/libstdc++-v3/include/bits/vector.tcc +++ b/libstdc++-v3/include/bits/vector.tcc @@ -664,8 +664,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { if (__n != 0) { - if (size_type(this->_M_impl._M_end_of_storage - - this->_M_impl._M_finish) >= __n) + if (__position.base() == this->_M_impl._M_finish) + _M_fill_append(__n, __x); + else if (size_type(this->_M_impl._M_end_of_storage + - this->_M_impl._M_finish) >= __n) { #if __cplusplus < 201103L value_type __x_copy = __x; @@ -760,6 +762,60 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER } } + template<typename _Tp, typename _Alloc> + _GLIBCXX20_CONSTEXPR + void + vector<_Tp, _Alloc>:: + _M_fill_append(size_type __n, const value_type& __x) + { + if (size_type(this->_M_impl._M_end_of_storage + - this->_M_impl._M_finish) >= __n) + { + _GLIBCXX_ASAN_ANNOTATE_GROW(__n); + this->_M_impl._M_finish = + std::__uninitialized_fill_n_a(this->_M_impl._M_finish, __n, __x, + _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_GREW(__n); + } + else + { + // Make local copies of these members because the compiler thinks + // the allocator can alter them if 'this' is globally reachable. + pointer __old_start = this->_M_impl._M_start; + pointer __old_finish = this->_M_impl._M_finish; + const size_type __old_size = __old_finish - __old_start; + + const size_type __len = + _M_check_len(__n, "vector::_M_fill_append"); + pointer __new_start(this->_M_allocate(__len)); + pointer __new_finish(__new_start + __old_size); + __try + { + // See _M_realloc_insert above. + __new_finish = std::__uninitialized_fill_n_a( + __new_finish, __n, __x, + _M_get_Tp_allocator()); + std::__uninitialized_move_if_noexcept_a( + __old_start, __old_finish, __new_start, + _M_get_Tp_allocator()); + } + __catch(...) + { + std::_Destroy(__new_start + __old_size, __new_finish, + _M_get_Tp_allocator()); + _M_deallocate(__new_start, __len); + __throw_exception_again; + } + std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; + _M_deallocate(__old_start, + this->_M_impl._M_end_of_storage - __old_start); + this->_M_impl._M_start = __new_start; + this->_M_impl._M_finish = __new_finish; + this->_M_impl._M_end_of_storage = __new_start + __len; + } + } + #if __cplusplus >= 201103L #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/moveable.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/moveable.cc index 8d0f9ae9bd1..343a298823e 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/moveable.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/moveable.cc @@ -109,9 +109,11 @@ test05() // when it doesn't reallocate the buffer. VERIFY(copycounter::copycount == 20 + 1); a.insert(a.end(), 50, c); - VERIFY(copycounter::copycount == 70 + 2); + // expect when inserting at the end (appending), where existing + // elements are not modified + VERIFY(copycounter::copycount == 70 + 1); a.insert(a.begin() + 50, 100, c); - VERIFY(copycounter::copycount == 170 + 3); + VERIFY(copycounter::copycount == 170 + 2); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/resize.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/resize.cc new file mode 100644 index 00000000000..f24b72ab646 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/resize.cc @@ -0,0 +1,69 @@ +// { dg-do run } + +#include <vector> +#include <testsuite_hooks.h> + +struct NoAssign +{ + NoAssign(int p) : val(p) {} + const int val; +}; + +struct PrivateAssign +{ + PrivateAssign(int p) : val(p) {} + PrivateAssign(const PrivateAssign& rhs) : val(rhs.val) {} + + int val; + +private: + PrivateAssign& operator=(const PrivateAssign&); +}; + +#if __cplusplus >= 201102L +struct DeletedAssign +{ + DeletedAssign(int p) : val(p) {} + DeletedAssign(const DeletedAssign& rhs) : val(rhs.val) {} + + DeletedAssign& operator=(const DeletedAssign&) = delete; + + int val; +}; +#endif + +template<typename T> +void +testPR90129() +{ + std::vector<T> v; + v.resize(5, T(5)); + VERIFY( v.size() == 5 ); + VERIFY( v.front().val == 5 ); + VERIFY( v.back().val == 5 ); + + v.resize(10, T(10)); + VERIFY( v.size() == 10 ); + VERIFY( v.front().val == 5 ); + VERIFY( v.back().val == 10 ); + + v.resize(7, T(7)); + VERIFY( v.size() == 7 ); + VERIFY( v.front().val == 5 ); + VERIFY( v.back().val == 10 ); + + v.resize(3, T(3)); + VERIFY( v.size() == 3 ); + VERIFY( v.front().val == 5 ); + VERIFY( v.back().val == 5 ); +} + +int main() +{ + testPR90129<NoAssign>(); + testPR90129<PrivateAssign>(); +#if __cplusplus >= 201102L + testPR90129<DeletedAssign>(); +#endif + return 0; +} -- 2.50.1