utils: chunked_vector: add from_range_t constructor
std::ranges::to<> has a little protocol with containers. Implement it to get optimized construction. Similar to the iterator pair constructor, if the range's size can be obtained (even with an O(N) algorithm), favor that to avoid reallocations. Copy elements spanwise to promote optimization to memcpy when possible.
This commit is contained in:
@@ -178,6 +178,19 @@ BOOST_AUTO_TEST_CASE(tests_constructor_exception_safety) {
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(tests_constructor_exception_safety_range) {
|
||||
auto checker = exception_safety_checker();
|
||||
auto v = std::vector<exception_safe_class>(100, exception_safe_class(checker));
|
||||
checker.set_countdown(5);
|
||||
try {
|
||||
auto u = utils::chunked_vector<exception_safe_class, 128>(std::from_range, v);
|
||||
BOOST_REQUIRE(false);
|
||||
} catch (...) {
|
||||
v.clear();
|
||||
BOOST_REQUIRE(checker.ok());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(tests_reserve_partial) {
|
||||
auto rand = std::default_random_engine();
|
||||
auto size_dist = std::uniform_int_distribution<unsigned>(1, 1 << 12);
|
||||
@@ -429,6 +442,13 @@ BOOST_AUTO_TEST_CASE(test_initializer_list_ctor) {
|
||||
BOOST_REQUIRE(vit == vec.end());
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_range_ctor) {
|
||||
auto range = std::views::iota(0, 12345);
|
||||
auto vec = range | std::ranges::to<utils::chunked_vector<int, 512>>();
|
||||
BOOST_REQUIRE(std::ranges::equal(range, vec));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_value_default_init_ctor) {
|
||||
int n = 17;
|
||||
auto vec = utils::chunked_vector<std::string, 8>(n);
|
||||
|
||||
@@ -104,6 +104,11 @@ public:
|
||||
chunked_vector(chunked_vector&& x) noexcept;
|
||||
template <typename Iterator>
|
||||
chunked_vector(Iterator begin, Iterator end);
|
||||
|
||||
template <std::ranges::range Range>
|
||||
requires std::convertible_to<std::ranges::range_value_t<Range>, T>
|
||||
chunked_vector(std::from_range_t, Range&& range);
|
||||
|
||||
chunked_vector(std::initializer_list<T> x);
|
||||
explicit chunked_vector(size_t n, const T& value = T());
|
||||
~chunked_vector();
|
||||
@@ -372,6 +377,28 @@ chunked_vector<T, max_contiguous_allocation>::chunked_vector(Iterator begin, Ite
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t max_contiguous_allocation>
|
||||
template <std::ranges::range Range>
|
||||
requires std::convertible_to<std::ranges::range_value_t<Range>, T>
|
||||
chunked_vector<T, max_contiguous_allocation>::chunked_vector(std::from_range_t, Range&& range)
|
||||
: chunked_vector() {
|
||||
if constexpr (std::ranges::forward_range<Range>) {
|
||||
size_t size = std::ranges::distance(range);
|
||||
reserve(size);
|
||||
auto begin = std::ranges::begin(range);
|
||||
for (size_t i = 0; _size < size; ++i) {
|
||||
T* dst = _chunks[i].get();
|
||||
auto now = std::min(size - _size, max_chunk_capacity());
|
||||
begin = std::ranges::uninitialized_copy_n(begin, now, dst, dst + now).in;
|
||||
// Update _size incrementally to let the destructor
|
||||
// know how much data to destroy on exception
|
||||
_size += now;
|
||||
}
|
||||
} else {
|
||||
std::ranges::copy(range, std::back_inserter(*this));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t max_contiguous_allocation>
|
||||
chunked_vector<T, max_contiguous_allocation>::chunked_vector(std::initializer_list<T> x)
|
||||
: chunked_vector(std::begin(x), std::end(x)) {
|
||||
|
||||
Reference in New Issue
Block a user