diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h index 9587f01f64d58..a02a88399a7a7 100644 --- a/libcxx/include/__memory/uninitialized_algorithms.h +++ b/libcxx/include/__memory/uninitialized_algorithms.h @@ -585,9 +585,9 @@ __uninitialized_allocator_copy_impl(_Alloc&, _In* __first1, _In* __last1, _Out* template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2 __uninitialized_allocator_copy(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) { - auto __unwrapped_range = std::__unwrap_range(__first1, __last1); + auto __unwrapped_range = std::__unwrap_range(std::move(__first1), std::move(__last1)); auto __result = std::__uninitialized_allocator_copy_impl( - __alloc, __unwrapped_range.first, __unwrapped_range.second, std::__unwrap_iter(__first2)); + __alloc, std::move(__unwrapped_range.first), std::move(__unwrapped_range.second), std::__unwrap_iter(__first2)); return std::__rewrap_iter(__first2, __result); } diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h index d94aca6788c87..66cb622e20963 100644 --- a/libcxx/include/__vector/vector.h +++ b/libcxx/include/__vector/vector.h @@ -10,11 +10,13 @@ #define _LIBCPP___VECTOR_VECTOR_H #include <__algorithm/copy.h> +#include <__algorithm/copy_n.h> #include <__algorithm/fill_n.h> #include <__algorithm/max.h> #include <__algorithm/min.h> #include <__algorithm/move.h> #include <__algorithm/move_backward.h> +#include <__algorithm/ranges_copy_n.h> #include <__algorithm/rotate.h> #include <__assert> #include <__config> @@ -23,6 +25,7 @@ #include <__fwd/vector.h> #include <__iterator/advance.h> #include <__iterator/bounded_iter.h> +#include <__iterator/concepts.h> #include <__iterator/distance.h> #include <__iterator/iterator_traits.h> #include <__iterator/move_iterator.h> @@ -575,7 +578,7 @@ class _LIBCPP_TEMPLATE_VIS vector { if (__n > 0) { __vallocate(__n); - __construct_at_end(__first, __last, __n); + __construct_at_end(std::move(__first), std::move(__last), __n); } __guard.__complete(); @@ -595,9 +598,12 @@ class _LIBCPP_TEMPLATE_VIS vector { template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __assign_with_sentinel(_Iterator __first, _Sentinel __last); - template + // The `_Iterator` in `*_with_size` functions can be input-only only if called from `*_range` (since C++23). + // Otherwise, `_Iterator` is a forward iterator. + + template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void - __assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __n); + __assign_with_size(_Iterator __first, _Sentinel __last, difference_type __n); template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator @@ -918,7 +924,7 @@ template _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(_InputIterator __first, _Sentinel __last, size_type __n) { _ConstructTransaction __tx(*this, __n); - __tx.__pos_ = std::__uninitialized_allocator_copy(this->__alloc_, __first, __last, __tx.__pos_); + __tx.__pos_ = std::__uninitialized_allocator_copy(this->__alloc_, std::move(__first), std::move(__last), __tx.__pos_); } // Default constructs __n objects starting at __end_ @@ -1027,23 +1033,28 @@ vector<_Tp, _Allocator>::__assign_with_sentinel(_Iterator __first, _Sentinel __l } template -template +template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void -vector<_Tp, _Allocator>::__assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __n) { +vector<_Tp, _Allocator>::__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __n) { size_type __new_size = static_cast(__n); if (__new_size <= capacity()) { if (__new_size > size()) { - _ForwardIterator __mid = std::next(__first, size()); +#if _LIBCPP_STD_VER >= 23 + auto __mid = ranges::copy_n(std::move(__first), size(), this->__begin_).in; + __construct_at_end(std::move(__mid), std::move(__last), __new_size - size()); +#else + _Iterator __mid = std::next(__first, size()); std::copy(__first, __mid, this->__begin_); __construct_at_end(__mid, __last, __new_size - size()); +#endif } else { - pointer __m = std::__copy(__first, __last, this->__begin_).second; + pointer __m = std::__copy(std::move(__first), __last, this->__begin_).second; this->__destruct_at_end(__m); } } else { __vdeallocate(); __vallocate(__recommend(__new_size)); - __construct_at_end(__first, __last, __new_size); + __construct_at_end(std::move(__first), std::move(__last), __new_size); } } @@ -1293,28 +1304,40 @@ template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator vector<_Tp, _Allocator>::__insert_with_size( const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n) { - auto __insertion_size = __n; - pointer __p = this->__begin_ + (__position - begin()); + pointer __p = this->__begin_ + (__position - begin()); if (__n > 0) { if (__n <= this->__cap_ - this->__end_) { - size_type __old_n = __n; pointer __old_last = this->__end_; - _Iterator __m = std::next(__first, __n); difference_type __dx = this->__end_ - __p; if (__n > __dx) { - __m = __first; - difference_type __diff = this->__end_ - __p; - std::advance(__m, __diff); - __construct_at_end(__m, __last, __n - __diff); - __n = __dx; - } - if (__n > 0) { - __move_range(__p, __old_last, __p + __old_n); - std::copy(__first, __m, __p); +#if _LIBCPP_STD_VER >= 23 + if constexpr (!forward_iterator<_Iterator>) { + __construct_at_end(std::move(__first), std::move(__last), __n); + std::rotate(__p, __old_last, this->__end_); + } else +#endif + { + _Iterator __m = std::next(__first, __dx); + __construct_at_end(__m, __last, __n - __dx); + if (__dx > 0) { + __move_range(__p, __old_last, __p + __n); + std::copy(__first, __m, __p); + } + } + } else { + __move_range(__p, __old_last, __p + __n); +#if _LIBCPP_STD_VER >= 23 + if constexpr (!forward_iterator<_Iterator>) { + ranges::copy_n(std::move(__first), __n, __p); + } else +#endif + { + std::copy_n(__first, __n, __p); + } } } else { __split_buffer __v(__recommend(size() + __n), __p - this->__begin_, this->__alloc_); - __v.__construct_at_end_with_size(__first, __insertion_size); + __v.__construct_at_end_with_size(std::move(__first), __n); __p = __swap_out_circular_buffer(__v, __p); } } diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h index 2b721e00058bc..c423e2e8cca46 100644 --- a/libcxx/include/__vector/vector_bool.h +++ b/libcxx/include/__vector/vector_bool.h @@ -420,9 +420,12 @@ class _LIBCPP_TEMPLATE_VIS vector { template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __assign_with_sentinel(_Iterator __first, _Sentinel __last); - template + // The `_Iterator` in `*_with_size` functions can be input-only only if called from `*_range` (since C++23). + // Otherwise, `_Iterator` is a forward iterator. + + template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void - __assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __ns); + __assign_with_size(_Iterator __first, _Sentinel __last, difference_type __ns); template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator @@ -578,7 +581,7 @@ vector::__construct_at_end(_InputIterator __first, _Sentinel _ else this->__begin_[(this->__size_ - 1) / __bits_per_word] = __storage_type(0); } - std::__copy(__first, __last, __make_iter(__old_size)); + std::__copy(std::move(__first), std::move(__last), __make_iter(__old_size)); } template @@ -828,9 +831,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector::assign(_ForwardIter } template -template +template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void -vector::__assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __ns) { +vector::__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __ns) { _LIBCPP_ASSERT_VALID_INPUT_RANGE(__ns >= 0, "invalid range specified"); clear(); @@ -841,7 +844,7 @@ vector::__assign_with_size(_ForwardIterator __first, _Sentinel __vdeallocate(); __vallocate(__n); } - __construct_at_end(__first, __last, __n); + __construct_at_end(std::move(__first), std::move(__last), __n); } } @@ -986,10 +989,10 @@ vector::insert(const_iterator __position, _ForwardIterator __f } template -template +template _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector::iterator vector::__insert_with_size( - const_iterator __position, _ForwardIterator __first, _Sentinel __last, difference_type __n_signed) { + const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n_signed) { _LIBCPP_ASSERT_VALID_INPUT_RANGE(__n_signed >= 0, "invalid range specified"); const size_type __n = static_cast(__n_signed); iterator __r; @@ -1007,7 +1010,7 @@ vector::__insert_with_size( std::copy_backward(__position, cend(), __v.end()); swap(__v); } - std::__copy(__first, __last, __r); + std::__copy(std::move(__first), std::move(__last), __r); return __r; } diff --git a/libcxx/test/std/containers/sequences/vector.bool/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/assign_range.pass.cpp index e5d0454a844d5..a8cbb32c2655e 100644 --- a/libcxx/test/std/containers/sequences/vector.bool/assign_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector.bool/assign_range.pass.cpp @@ -12,6 +12,7 @@ // template R> // constexpr void assign_range(R&& rg); // C++23 +#include #include #include "../insert_range_sequence_containers.h" @@ -49,11 +50,29 @@ constexpr bool test() { v.assign_range(in); assert(std::ranges::equal(v, in)); } + + { // Ensure input-only sized ranges are accepted. + using input_iter = cpp20_input_iterator; + const bool in[]{true, true, false, true}; + std::vector v; + v.assign_range(std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in))); + assert(std::ranges::equal(v, std::vector{true, true, false, true})); + } } return true; } +#ifndef TEST_HAS_NO_LOCALIZATION +void test_counted_istream_view() { + std::istringstream is{"1 1 0 1"}; + auto vals = std::views::istream(is); + std::vector v; + v.assign_range(std::views::counted(vals.begin(), 3)); + assert(v == (std::vector{true, true, false})); +} +#endif + int main(int, char**) { test(); static_assert(test()); @@ -61,5 +80,9 @@ int main(int, char**) { // Note: `test_assign_range_exception_safety_throwing_copy` doesn't apply because copying booleans cannot throw. test_assign_range_exception_safety_throwing_allocator(); +#ifndef TEST_HAS_NO_LOCALIZATION + test_counted_istream_view(); +#endif + return 0; } diff --git a/libcxx/test/std/containers/sequences/vector.bool/construct_from_range.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/construct_from_range.pass.cpp index 03f3100b92883..055f2378125d9 100644 --- a/libcxx/test/std/containers/sequences/vector.bool/construct_from_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector.bool/construct_from_range.pass.cpp @@ -8,6 +8,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +#include #include #include "../from_range_sequence_containers.h" @@ -24,9 +25,25 @@ constexpr bool test() { }); }); + { // Ensure input-only sized ranges are accepted. + using input_iter = cpp20_input_iterator; + const bool in[]{true, true, false, true}; + std::vector v(std::from_range, std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in))); + assert(std::ranges::equal(v, std::vector{true, true, false, true})); + } + return true; } +#ifndef TEST_HAS_NO_LOCALIZATION +void test_counted_istream_view() { + std::istringstream is{"1 1 0 1"}; + auto vals = std::views::istream(is); + std::vector v(std::from_range, std::views::counted(vals.begin(), 3)); + assert(v == (std::vector{true, true, false})); +} +#endif + int main(int, char**) { test(); static_assert(test()); @@ -36,5 +53,9 @@ int main(int, char**) { // Note: test_exception_safety_throwing_copy doesn't apply because copying a boolean cannot throw. test_exception_safety_throwing_allocator(); +#ifndef TEST_HAS_NO_LOCALIZATION + test_counted_istream_view(); +#endif + return 0; } diff --git a/libcxx/test/std/containers/sequences/vector.bool/insert_range.pass.cpp b/libcxx/test/std/containers/sequences/vector.bool/insert_range.pass.cpp index 65d085fa1f083..d8f19d47e9942 100644 --- a/libcxx/test/std/containers/sequences/vector.bool/insert_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector.bool/insert_range.pass.cpp @@ -12,6 +12,7 @@ // template R> // constexpr iterator insert_range(const_iterator position, R&& rg); // C++23 +#include #include #include "../insert_range_sequence_containers.h" @@ -56,11 +57,29 @@ constexpr bool test() { v.insert_range(v.end(), in); assert(std::ranges::equal(v, std::vector{0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1})); } + + { // Ensure input-only sized ranges are accepted. + using input_iter = cpp20_input_iterator; + const bool in[]{true, true, false, true}; + std::vector v{true, false}; + v.insert_range(v.begin(), std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in))); + assert(std::ranges::equal(v, std::vector{true, true, false, true, true, false})); + } } return true; } +#ifndef TEST_HAS_NO_LOCALIZATION +void test_counted_istream_view() { + std::istringstream is{"1 1 0 1"}; + auto vals = std::views::istream(is); + std::vector v; + v.insert_range(v.end(), std::views::counted(vals.begin(), 3)); + assert(v == (std::vector{true, true, false})); +} +#endif + int main(int, char**) { test(); static_assert(test()); @@ -68,5 +87,9 @@ int main(int, char**) { // Note: `test_insert_range_exception_safety_throwing_copy` doesn't apply because copying booleans cannot throw. test_insert_range_exception_safety_throwing_allocator(); +#ifndef TEST_HAS_NO_LOCALIZATION + test_counted_istream_view(); +#endif + return 0; } diff --git a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_from_range.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_from_range.pass.cpp index 5fb2b46f7e942..501abf396391f 100644 --- a/libcxx/test/std/containers/sequences/vector/vector.cons/construct_from_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector/vector.cons/construct_from_range.pass.cpp @@ -11,6 +11,7 @@ // template R> // vector(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23 +#include #include #include "../../from_range_sequence_containers.h" @@ -26,9 +27,25 @@ constexpr bool test() { }); test_sequence_container_move_only(); + { // Ensure input-only sized ranges are accepted. + using input_iter = cpp20_input_iterator; + const int in[]{1, 2, 3, 4}; + std::vector v(std::from_range, std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in))); + assert(std::ranges::equal(v, std::vector{1, 2, 3, 4})); + } + return true; } +#ifndef TEST_HAS_NO_LOCALIZATION +void test_counted_istream_view() { + std::istringstream is{"1 2 3 4"}; + auto vals = std::views::istream(is); + std::vector v(std::from_range, std::views::counted(vals.begin(), 3)); + assert(v == (std::vector{1, 2, 3})); +} +#endif + int main(int, char**) { static_assert(test_constraints()); test(); @@ -38,5 +55,9 @@ int main(int, char**) { test_exception_safety_throwing_copy(); test_exception_safety_throwing_allocator(); +#ifndef TEST_HAS_NO_LOCALIZATION + test_counted_istream_view(); +#endif + return 0; } diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/assign_range.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/assign_range.pass.cpp index 8ab3dc10aed99..6326ffca96966 100644 --- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/assign_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/assign_range.pass.cpp @@ -12,6 +12,7 @@ // template R> // constexpr void assign_range(R&& rg); // C++23 +#include #include #include "../../insert_range_sequence_containers.h" @@ -62,11 +63,29 @@ constexpr bool test() { v.assign_range(in); assert(std::ranges::equal(v, in)); } + + { // Ensure input-only sized ranges are accepted. + using input_iter = cpp20_input_iterator; + const int in[]{1, 2, 3, 4}; + std::vector v; + v.assign_range(std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in))); + assert(std::ranges::equal(v, std::vector{1, 2, 3, 4})); + } } return true; } +#ifndef TEST_HAS_NO_LOCALIZATION +void test_counted_istream_view() { + std::istringstream is{"1 2 3 4"}; + auto vals = std::views::istream(is); + std::vector v; + v.assign_range(std::views::counted(vals.begin(), 3)); + assert(v == (std::vector{1, 2, 3})); +} +#endif + int main(int, char**) { test(); static_assert(test()); @@ -74,5 +93,9 @@ int main(int, char**) { test_assign_range_exception_safety_throwing_copy(); test_assign_range_exception_safety_throwing_allocator(); +#ifndef TEST_HAS_NO_LOCALIZATION + test_counted_istream_view(); +#endif + return 0; } diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp index 0e26cb1546277..25ffc33aad424 100644 --- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp @@ -12,6 +12,7 @@ // template R> // constexpr iterator insert_range(const_iterator position, R&& rg); // C++23 +#include #include #include "../../insert_range_sequence_containers.h" @@ -53,11 +54,29 @@ constexpr bool test() { v.insert_range(v.end(), in); assert(std::ranges::equal(v, std::array{1, 2, 3, 4, 5, 6, 7, 8, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10})); } + + { // Ensure input-only sized ranges are accepted. + using input_iter = cpp20_input_iterator; + const int in[]{1, 2, 3, 4}; + std::vector v{-5, -6}; + v.insert_range(v.begin(), std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in))); + assert(std::ranges::equal(v, std::vector{1, 2, 3, 4, -5, -6})); + } } return true; } +#ifndef TEST_HAS_NO_LOCALIZATION +void test_counted_istream_view() { + std::istringstream is{"1 2 3 4"}; + auto vals = std::views::istream(is); + std::vector v; + v.insert_range(v.end(), std::views::counted(vals.begin(), 3)); + assert(v == (std::vector{1, 2, 3})); +} +#endif + int main(int, char**) { test(); static_assert(test()); @@ -67,5 +86,9 @@ int main(int, char**) { test_insert_range_exception_safety_throwing_copy(); test_insert_range_exception_safety_throwing_allocator(); +#ifndef TEST_HAS_NO_LOCALIZATION + test_counted_istream_view(); +#endif + return 0; }