Title Text
The C++20 Range Library
from Range-v3 to the future
Po Yen Chen @poyenc
© Po Yen Chen 2020
Background image ©
What is a range?
-
pair of iterators
- manipulate elements through iterators
© Po Yen Chen 2020
Background image ©
© Po Yen Chen 2020
Background image ©
Different iterator category matters
-
InputIterator vs OutputIterator
-
InputIterator vs ForwardIterator
template <typename Iterator> requires std::output_iterator<Iterator, int>
void assign_zero(Iterator it) {
*it;
*it = 0;
}template <typename Iterator> requires std::forward_iterator<Iterator>
void check_multi_pass(Iterator it) {
decltype(auto) before = *it;
std::next(it);
decltype(auto) after = *it;
assert(std::addressof(before) == std::addressof(after));
}© Po Yen Chen 2020
Background image ©
Liveness of range and iterators
const auto pi = [] {
std::vector<int> range;
return begin(range);
}();
// WARNING: its danger to use *pi from now
const auto pc = [] {
auto range = "hello"sv;
return begin(range);
}();
// ok to use *pc anywhere- range and iterators live independently
- range may owns elements
© Po Yen Chen 2020
Background image ©
Ranges in concrete
- Most STL containers e.g. std::vector, std::deque
- With owning semantics
- std::basic_string
- With non-owning semantics
- std::span
- std::basic_string_view
© Po Yen Chen 2020
Background image ©
Ranges in abstract
std::vector<int> ints(10);
std::iota(begin(ints), end(ints), 1);
// create range of descending integers
assert(std::is_sorted(std::make_reverse_iterator(end(ints)),
std::make_reverse_iterator(begin(ints)),
std::greater<>()));
std::istringstream stream("96 27");
// create range of arbitrary integers
assert(std::accumulate(std::istream_iterator<int>(stream),
std::istream_iterator<int>(), 0) == 123);There are no range-based libraries until C++20
© Po Yen Chen 2020
Background image ©
The problem of iterator-based library
std::vector<unsigned> scores;
// fill score values here
decltype(scores) squared;
std::transform(
begin(scores), end(scores), std::back_inserter(squared),
[](auto score) { return std::sqrt(score); }
);
decltype(scores) enlarged;
std::transform(
begin(squared), end(squared), std::back_inserter(enlarged),
[](auto score) { return score * 10; }
);
std::cout << std::count_if(begin(enlarged), end(enlarged),
[](auto score) { return 60 <= score; })
<< std::endl;- Lots of intermediate values
- Hard to find business logics
© Po Yen Chen 2020
Background image ©
Rewrite by range-based library
std::vector<unsigned> scores;
// fill score values here
std::cout << std::ranges::count_if(
scores
| ranges::view::transform([](auto score) { return std::sqrt(score); })
| ranges::view::transform([](auto score) { return score * 10; })
, [](auto score) { return 60 <= score; }
) << std::endl;- Better maintainability
- More efficiency
© Po Yen Chen 2020
Background image ©
The Range-v3 library contains...
- Algorithms
- range-based STL algorithms
- Actions
- in-place modifying range-based STL algorithms
- Views
- composable adaptations of ranges
- lazy evaluation
More are coming in C++23
© Po Yen Chen 2020
Background image ©
Two more things...
-
The Customization Point
template <typename T>
void two_step_swap(T& a, T& b) {
using std::swap; // overload functions in namespace std are also candidates
swap(a, b); // customization point, may find match through ADL
}
namespace ns {
struct Foo {};
void swap(Foo&, Foo&) { // customized function
puts("customized swap");
}
}
ns::Foo a, b;
::two_step_swap(a, b); // ok to use qualified name here© Po Yen Chen 2020
Background image ©
Two more things...
-
The Customization Point Object
const std::vector values{1.1, 2.2, 3.3};
std::ranges::transform(values, ranges::ostream_iterator<>(std::cout, " "),
std::floor);
// ^^^^^^^^^^ error: unresolved overloaded function typemodified version
inline constexpr struct {
template <typename T>
auto operator()(T value) const {
using std::floor;
return floor(value); // may invoke a customized function for T
}
} two_step_floor;
std::ranges::transform(values, ranges::ostream_iterator<>(std::cout, " "),
two_step_floor);© Po Yen Chen 2020
Background image ©
The example: ranges::view::cycle
- Generate ring-buffer access indices
#include <iostream>
#include <ranges>
#include <range/v3/iterator/stream_iterators.hpp>
#include <range/v3/view/cycle.hpp>
#include <range/v3/view/indices.hpp>
#include <range/v3/view/take.hpp>
int main() {
std::ranges::copy(ranges::view::indices(5) // ring-buffer size = 5
| ranges::view::cycle
| ranges::view::take(10), // desired sequence length = 10
ranges::ostream_iterator<>(std::cout, " "));
}Let's dive into the source : cycle.hpp
© Po Yen Chen 2020
Background image ©
Inspect the source files
- view_closure: view/view.hpp:174
- all_t: view/all.hpp:92
- view_facade: view/facade.hpp:65
- non_propagating_cache: utility/optional.hpp:908
- common_range: range/concepts.hpp:144
- range_access: detail/range_access.hpp:35
- next: iterator/operations.hpp:335
© Po Yen Chen 2020
Background image ©
Toward to range
- More range-based algorithms
- take single range
- take iterator & size pair
- Conversion from range to concrete types
- use ranges::to() to generate containers
- add ctor to accept range argument
- Additional projection parameter for algorithms
© Po Yen Chen 2020
Background image ©
References
- [P0896R4] The One Ranges Proposal
- [Eric Niebler's blog] Customization Point Design in C++11 and Beyond
- [P2046R0] Rangify New Algorithms

The C++20 Range Library: from Range-v3 to the future
By Po-Yen, Chen
The C++20 Range Library: from Range-v3 to the future
COSCUP 2020 presentation
- 211