Skip to content

Commit fc058a8

Browse files
[libc++] Fix input-only range handling for vector
Changes: - Carve out sized but input-only ranges for C++23. - Call `std::move` for related functions when the iterator is possibly input-only.
1 parent 52361d0 commit fc058a8

File tree

9 files changed

+199
-36
lines changed

9 files changed

+199
-36
lines changed

libcxx/include/__memory/uninitialized_algorithms.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -585,9 +585,9 @@ __uninitialized_allocator_copy_impl(_Alloc&, _In* __first1, _In* __last1, _Out*
585585
template <class _Alloc, class _Iter1, class _Sent1, class _Iter2>
586586
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2
587587
__uninitialized_allocator_copy(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
588-
auto __unwrapped_range = std::__unwrap_range(__first1, __last1);
588+
auto __unwrapped_range = std::__unwrap_range(std::move(__first1), std::move(__last1));
589589
auto __result = std::__uninitialized_allocator_copy_impl(
590-
__alloc, __unwrapped_range.first, __unwrapped_range.second, std::__unwrap_iter(__first2));
590+
__alloc, std::move(__unwrapped_range.first), std::move(__unwrapped_range.second), std::__unwrap_iter(__first2));
591591
return std::__rewrap_iter(__first2, __result);
592592
}
593593

libcxx/include/__vector/vector.h

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
#define _LIBCPP___VECTOR_VECTOR_H
1111

1212
#include <__algorithm/copy.h>
13+
#include <__algorithm/copy_n.h>
1314
#include <__algorithm/fill_n.h>
1415
#include <__algorithm/max.h>
1516
#include <__algorithm/min.h>
1617
#include <__algorithm/move.h>
1718
#include <__algorithm/move_backward.h>
19+
#include <__algorithm/ranges_copy_n.h>
1820
#include <__algorithm/rotate.h>
1921
#include <__assert>
2022
#include <__config>
@@ -23,6 +25,7 @@
2325
#include <__fwd/vector.h>
2426
#include <__iterator/advance.h>
2527
#include <__iterator/bounded_iter.h>
28+
#include <__iterator/concepts.h>
2629
#include <__iterator/distance.h>
2730
#include <__iterator/iterator_traits.h>
2831
#include <__iterator/move_iterator.h>
@@ -570,7 +573,7 @@ class _LIBCPP_TEMPLATE_VIS vector {
570573

571574
if (__n > 0) {
572575
__vallocate(__n);
573-
__construct_at_end(__first, __last, __n);
576+
__construct_at_end(std::move(__first), std::move(__last), __n);
574577
}
575578

576579
__guard.__complete();
@@ -590,9 +593,12 @@ class _LIBCPP_TEMPLATE_VIS vector {
590593
template <class _Iterator, class _Sentinel>
591594
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __assign_with_sentinel(_Iterator __first, _Sentinel __last);
592595

593-
template <class _ForwardIterator, class _Sentinel>
596+
// The `_Iterator` in `*_with_size` functions can be input-only only if called from `*_range` (since C++23).
597+
// Otherwise, `_Iterator` is a forward iterator.
598+
599+
template <class _Iterator, class _Sentinel>
594600
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
595-
__assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __n);
601+
__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __n);
596602

597603
template <class _InputIterator, class _Sentinel>
598604
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
@@ -906,7 +912,7 @@ template <class _InputIterator, class _Sentinel>
906912
_LIBCPP_CONSTEXPR_SINCE_CXX20 void
907913
vector<_Tp, _Allocator>::__construct_at_end(_InputIterator __first, _Sentinel __last, size_type __n) {
908914
_ConstructTransaction __tx(*this, __n);
909-
__tx.__pos_ = std::__uninitialized_allocator_copy(this->__alloc_, __first, __last, __tx.__pos_);
915+
__tx.__pos_ = std::__uninitialized_allocator_copy(this->__alloc_, std::move(__first), std::move(__last), __tx.__pos_);
910916
}
911917

912918
// Default constructs __n objects starting at __end_
@@ -1011,23 +1017,31 @@ vector<_Tp, _Allocator>::__assign_with_sentinel(_Iterator __first, _Sentinel __l
10111017
}
10121018

10131019
template <class _Tp, class _Allocator>
1014-
template <class _ForwardIterator, class _Sentinel>
1020+
template <class _Iterator, class _Sentinel>
10151021
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
1016-
vector<_Tp, _Allocator>::__assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __n) {
1022+
vector<_Tp, _Allocator>::__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __n) {
10171023
size_type __new_size = static_cast<size_type>(__n);
10181024
if (__new_size <= capacity()) {
10191025
if (__new_size > size()) {
1020-
_ForwardIterator __mid = std::next(__first, size());
1021-
std::copy(__first, __mid, this->__begin_);
1022-
__construct_at_end(__mid, __last, __new_size - size());
1026+
#if _LIBCPP_STD_VER >= 23
1027+
if constexpr (!forward_iterator<_Iterator>) {
1028+
auto __mid = ranges::copy_n(std::move(__first), size(), this->__begin_).in;
1029+
__construct_at_end(std::move(__mid), std::move(__last), __new_size - size());
1030+
} else
1031+
#endif
1032+
{
1033+
_Iterator __mid = std::next(__first, size());
1034+
std::copy(__first, __mid, this->__begin_);
1035+
__construct_at_end(__mid, __last, __new_size - size());
1036+
}
10231037
} else {
1024-
pointer __m = std::__copy(__first, __last, this->__begin_).second;
1038+
pointer __m = std::__copy(std::move(__first), __last, this->__begin_).second;
10251039
this->__destruct_at_end(__m);
10261040
}
10271041
} else {
10281042
__vdeallocate();
10291043
__vallocate(__recommend(__new_size));
1030-
__construct_at_end(__first, __last, __new_size);
1044+
__construct_at_end(std::move(__first), std::move(__last), __new_size);
10311045
}
10321046
}
10331047

@@ -1277,28 +1291,40 @@ template <class _Iterator, class _Sentinel>
12771291
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
12781292
vector<_Tp, _Allocator>::__insert_with_size(
12791293
const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n) {
1280-
auto __insertion_size = __n;
1281-
pointer __p = this->__begin_ + (__position - begin());
1294+
pointer __p = this->__begin_ + (__position - begin());
12821295
if (__n > 0) {
12831296
if (__n <= this->__cap_ - this->__end_) {
1284-
size_type __old_n = __n;
12851297
pointer __old_last = this->__end_;
1286-
_Iterator __m = std::next(__first, __n);
12871298
difference_type __dx = this->__end_ - __p;
12881299
if (__n > __dx) {
1289-
__m = __first;
1290-
difference_type __diff = this->__end_ - __p;
1291-
std::advance(__m, __diff);
1292-
__construct_at_end(__m, __last, __n - __diff);
1293-
__n = __dx;
1294-
}
1295-
if (__n > 0) {
1296-
__move_range(__p, __old_last, __p + __old_n);
1297-
std::copy(__first, __m, __p);
1300+
#if _LIBCPP_STD_VER >= 23
1301+
if constexpr (!forward_iterator<_Iterator>) {
1302+
__construct_at_end(std::move(__first), std::move(__last), __n);
1303+
std::rotate(__p, __old_last, this->__end_);
1304+
} else
1305+
#endif
1306+
{
1307+
_Iterator __m = std::next(__first, __dx);
1308+
__construct_at_end(__m, __last, __n - __dx);
1309+
if (__dx > 0) {
1310+
__move_range(__p, __old_last, __p + __n);
1311+
std::copy(__first, __m, __p);
1312+
}
1313+
}
1314+
} else {
1315+
__move_range(__p, __old_last, __p + __n);
1316+
#if _LIBCPP_STD_VER >= 23
1317+
if constexpr (!forward_iterator<_Iterator>) {
1318+
ranges::copy_n(std::move(__first), __n, __p);
1319+
} else
1320+
#endif
1321+
{
1322+
std::copy_n(__first, __n, __p);
1323+
}
12981324
}
12991325
} else {
13001326
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + __n), __p - this->__begin_, this->__alloc_);
1301-
__v.__construct_at_end_with_size(__first, __insertion_size);
1327+
__v.__construct_at_end_with_size(std::move(__first), __n);
13021328
__p = __swap_out_circular_buffer(__v, __p);
13031329
}
13041330
}

libcxx/include/__vector/vector_bool.h

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,12 @@ class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator> {
407407
template <class _Iterator, class _Sentinel>
408408
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __assign_with_sentinel(_Iterator __first, _Sentinel __last);
409409

410-
template <class _ForwardIterator, class _Sentinel>
410+
// The `_Iterator` in `*_with_size` functions can be input-only only if called from `*_range` (since C++23).
411+
// Otherwise, `_Iterator` is a forward iterator.
412+
413+
template <class _Iterator, class _Sentinel>
411414
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
412-
__assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __ns);
415+
__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __ns);
413416

414417
template <class _InputIterator, class _Sentinel>
415418
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
@@ -566,7 +569,7 @@ vector<bool, _Allocator>::__construct_at_end(_InputIterator __first, _Sentinel _
566569
else
567570
this->__begin_[(this->__size_ - 1) / __bits_per_word] = __storage_type(0);
568571
}
569-
std::__copy(__first, __last, __make_iter(__old_size));
572+
std::__copy(std::move(__first), std::move(__last), __make_iter(__old_size));
570573
}
571574

572575
template <class _Allocator>
@@ -816,9 +819,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::assign(_ForwardIter
816819
}
817820

818821
template <class _Allocator>
819-
template <class _ForwardIterator, class _Sentinel>
822+
template <class _Iterator, class _Sentinel>
820823
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
821-
vector<bool, _Allocator>::__assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __ns) {
824+
vector<bool, _Allocator>::__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __ns) {
822825
_LIBCPP_ASSERT_VALID_INPUT_RANGE(__ns >= 0, "invalid range specified");
823826

824827
clear();
@@ -829,7 +832,7 @@ vector<bool, _Allocator>::__assign_with_size(_ForwardIterator __first, _Sentinel
829832
__vdeallocate();
830833
__vallocate(__n);
831834
}
832-
__construct_at_end(__first, __last, __n);
835+
__construct_at_end(std::move(__first), std::move(__last), __n);
833836
}
834837
}
835838

@@ -973,10 +976,10 @@ vector<bool, _Allocator>::insert(const_iterator __position, _ForwardIterator __f
973976
}
974977

975978
template <class _Allocator>
976-
template <class _ForwardIterator, class _Sentinel>
979+
template <class _Iterator, class _Sentinel>
977980
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<bool, _Allocator>::iterator
978981
vector<bool, _Allocator>::__insert_with_size(
979-
const_iterator __position, _ForwardIterator __first, _Sentinel __last, difference_type __n_signed) {
982+
const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n_signed) {
980983
_LIBCPP_ASSERT_VALID_INPUT_RANGE(__n_signed >= 0, "invalid range specified");
981984
const size_type __n = static_cast<size_type>(__n_signed);
982985
iterator __r;
@@ -994,7 +997,7 @@ vector<bool, _Allocator>::__insert_with_size(
994997
std::copy_backward(__position, cend(), __v.end());
995998
swap(__v);
996999
}
997-
std::__copy(__first, __last, __r);
1000+
std::__copy(std::move(__first), std::move(__last), __r);
9981001
return __r;
9991002
}
10001003

libcxx/test/std/containers/sequences/vector.bool/assign_range.pass.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// template<container-compatible-range<bool> R>
1313
// constexpr void assign_range(R&& rg); // C++23
1414

15+
#include <sstream>
1516
#include <vector>
1617

1718
#include "../insert_range_sequence_containers.h"
@@ -49,17 +50,39 @@ constexpr bool test() {
4950
v.assign_range(in);
5051
assert(std::ranges::equal(v, in));
5152
}
53+
54+
{ // Ensure input-only sized ranges are accepted.
55+
using input_iter = cpp20_input_iterator<const bool*>;
56+
const bool in[]{true, true, false, true};
57+
std::vector<bool> v;
58+
v.assign_range(std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in)));
59+
assert(std::ranges::equal(v, std::vector<bool>{true, true, false, true}));
60+
}
5261
}
5362

5463
return true;
5564
}
5665

66+
#ifndef TEST_HAS_NO_LOCALIZATION
67+
void test_counted_istream_view() {
68+
std::istringstream is{"1 1 0 1"};
69+
auto vals = std::views::istream<bool>(is);
70+
std::vector<bool> v;
71+
v.assign_range(std::views::counted(vals.begin(), 3));
72+
assert(v == (std::vector{true, true, false}));
73+
}
74+
#endif
75+
5776
int main(int, char**) {
5877
test();
5978
static_assert(test());
6079

6180
// Note: `test_assign_range_exception_safety_throwing_copy` doesn't apply because copying booleans cannot throw.
6281
test_assign_range_exception_safety_throwing_allocator<std::vector, bool>();
6382

83+
#ifndef TEST_HAS_NO_LOCALIZATION
84+
test_counted_istream_view();
85+
#endif
86+
6487
return 0;
6588
}

libcxx/test/std/containers/sequences/vector.bool/construct_from_range.pass.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
1010

11+
#include <sstream>
1112
#include <vector>
1213

1314
#include "../from_range_sequence_containers.h"
@@ -24,9 +25,25 @@ constexpr bool test() {
2425
});
2526
});
2627

28+
{ // Ensure input-only sized ranges are accepted.
29+
using input_iter = cpp20_input_iterator<const bool*>;
30+
const bool in[]{true, true, false, true};
31+
std::vector v(std::from_range, std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in)));
32+
assert(std::ranges::equal(v, std::vector<bool>{true, true, false, true}));
33+
}
34+
2735
return true;
2836
}
2937

38+
#ifndef TEST_HAS_NO_LOCALIZATION
39+
void test_counted_istream_view() {
40+
std::istringstream is{"1 1 0 1"};
41+
auto vals = std::views::istream<bool>(is);
42+
std::vector v(std::from_range, std::views::counted(vals.begin(), 3));
43+
assert(v == (std::vector{true, true, false}));
44+
}
45+
#endif
46+
3047
int main(int, char**) {
3148
test();
3249
static_assert(test());
@@ -36,5 +53,9 @@ int main(int, char**) {
3653
// Note: test_exception_safety_throwing_copy doesn't apply because copying a boolean cannot throw.
3754
test_exception_safety_throwing_allocator<std::vector, bool>();
3855

56+
#ifndef TEST_HAS_NO_LOCALIZATION
57+
test_counted_istream_view();
58+
#endif
59+
3960
return 0;
4061
}

libcxx/test/std/containers/sequences/vector.bool/insert_range.pass.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// template<container-compatible-range<bool> R>
1313
// constexpr iterator insert_range(const_iterator position, R&& rg); // C++23
1414

15+
#include <sstream>
1516
#include <vector>
1617

1718
#include "../insert_range_sequence_containers.h"
@@ -56,17 +57,39 @@ constexpr bool test() {
5657
v.insert_range(v.end(), in);
5758
assert(std::ranges::equal(v, std::vector<bool>{0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1}));
5859
}
60+
61+
{ // Ensure input-only sized ranges are accepted.
62+
using input_iter = cpp20_input_iterator<const bool*>;
63+
const bool in[]{true, true, false, true};
64+
std::vector<bool> v{true, false};
65+
v.insert_range(v.begin(), std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in)));
66+
assert(std::ranges::equal(v, std::vector<bool>{true, true, false, true, true, false}));
67+
}
5968
}
6069

6170
return true;
6271
}
6372

73+
#ifndef TEST_HAS_NO_LOCALIZATION
74+
void test_counted_istream_view() {
75+
std::istringstream is{"1 1 0 1"};
76+
auto vals = std::views::istream<bool>(is);
77+
std::vector<bool> v;
78+
v.insert_range(v.end(), std::views::counted(vals.begin(), 3));
79+
assert(v == (std::vector{true, true, false}));
80+
}
81+
#endif
82+
6483
int main(int, char**) {
6584
test();
6685
static_assert(test());
6786

6887
// Note: `test_insert_range_exception_safety_throwing_copy` doesn't apply because copying booleans cannot throw.
6988
test_insert_range_exception_safety_throwing_allocator<std::vector, bool>();
7089

90+
#ifndef TEST_HAS_NO_LOCALIZATION
91+
test_counted_istream_view();
92+
#endif
93+
7194
return 0;
7295
}

0 commit comments

Comments
 (0)