effective_ranges_cppcon2023
effective_ranges_cppcon2023
2.1
hello ranges
2.2
the old way: sort
std::array<int, 6> a { 6, 2, 3, 4, 5, 1 };
for ( auto v : a )
cout << v << " " << endl;
2.3
the ranges way: sort
namespace rng = std::ranges;
std::array<int, 6> a { 6, 2, 3, 4, 5, 1 };
https://godbolt.org/z/o478PEMff
2.4
the old way: find_if
std::array<int, 6> a { 6, 2, 3, 4, 5, 1 };
if (i != std::end( v ) )
{
cout << "i: " << *i << endl;
}
2.5
the ranges way: find_if
namespace rng = std::ranges;
std::array<int, 6> a { 6, 2, 3, 4, 5, 1 };
if ( i != rng::end( a ) )
{
std::print( "i: {}\n", *i ); //i: 6
}
https://godbolt.org/z/4KEM3W939
2.6
the ranges way: filter_view
std::array<int, 6> a { 6, 2, 3, 4, 5, 1 };
using std::views::filter;
auto divisible_by3 = filter([](int i) -> bool { return i%3 == 0; } );
https://godbolt.org/z/bnchn9jaf
2.7
talk outline
range basics & concepts
range algo details and survey
range views details and survey
adjuncts
observations & futures
2.8
Range Basics
3.1
ranges, algorithms, views, adaptors
range: something that can be iterated over
range algo: algorithm that takes range
view: lazy range thats cheap (to copy)
range adaptor: make a range into a view
3.2
mechanics - headers, namespaces
#include <ranges> // new header for ranges and views
namespace std {
namespace ranges {...}; //improved algorithms and views
namespace ranges::views {...}; //range adaptors
namespace views = ranges::views; //shortcut to adaptors
}
3.3
what's a range?
iterator pair ex: { rbegin, rend }
an object with begin(), end() that returns
iterators
technically an iterator and a sentinel
sentinel and iterator can be different types
3.4
Examples of ranges
standard library things
containers: array, vector, map, set, list
container adaptors: queue, stack,
priority_queue
strings: string and string_view
fs::directory_iterator, stream iterators,
regex iterators
span, mdspan
many libraries outside std
3.5
boost::flat_map example
flat_map<string, int> fm;
fm["world"] = 2;
fm["hello"] = 1;
https://godbolt.org/z/Mdqh58zrM
3.6
range algorithms
are like STL algorithms
not always a drop in replacement
execute immediately
iteration is interally controlled by algorithm
added projection arguments
return values improved
3.7
range concepts: sized, etc
range - provides a begin iterator and an end sentinel
input_range - speci es that a range has
InputIterator
sized_range - speci es that a range knows its size
in constant time
random_access_range - speci es a range whose
iterator type satis es random_access_iterator
contiguous_range - speci es a range whose
iterator type satis es contiguous_iterator
3.8
Range Algorithms Details
and Survey
4.1
range algorithms are familiar
derived from the STL algorithms, but better
improved return information compared to STL for
some algos
speci ed with concepts
4.2
cheatsheet
4.3
cheatsheet
4.4
projection parameters
ltering predicate independent of function being
invoked
comes for the Adobe Source Libraries (ASL)
4.5
for example - sort with projection
struct stuff {
int idx = 0;
string s;
};
//lambda form
ranges::sort(vstuff, std::less<>{},
[](auto const& iii) { return iii.idx; });
//short form
ranges::sort( vstuff, std::less<>{}, &stuff::idx );
https://godbolt.org/z/8cvoEvd11
4.6
improved return values
principle of not discarding calculation
many ranges algorithm return a struct with
iterator+info
find_last example
nd with reverse iterators is suprisingly dif cult
boost.algorithm has one already call
find_backward
what should the return value be?
4.7
find_last example
std::string path = "/foo/bar/baz";
path = "none";
leaf = rng::find_last(path, '/')
| std::views::drop(1); //empty range
std::print("empty: {}", std::string_view(leaf)); //empty:
returns a range
https://godbolt.org/z/8KPjqYrG1
4.8
Views and Adaptor Details
5.1
views are ranges with 'lazy evaluation'
non-owning of elements (mostly)
all methods O(1) - copy and assignment
allows for composition of several processing steps
allows for 'in nite ranges'
because: iteration is externally driven
functional or declarative style versus imperative
5.2
constructed filter_view example
int main() {
std::vector<int> vi{ 0, 1, 2, 3, 4, 5, 6 };
5.3
range adaptors -> views from ranges
transform Range to View - with custom behaviors
adaptors support the piping operator
pipe syntax allows for 'unix pipe/powershell' type
composition
range adaptors are declared in namespace
std::ranges::views.
5.4
views::filter adpator example
std::vector<int> vi{ 0, 1, 2, 3, 4, 5, 6 };
5.5
range adaptors execution trace
vector<int> vec_int{ 0, 1, 2, 3, 4, 5 };
//annotated
auto even = [](int i) -> bool { cout << "ev:" << i << "\n";
return 0 == i % 2; };
auto square = [](int i) -> int { cout << "sq:" << i << "\n";
return i * i; };
https://godbolt.org/z/1863ce6sW
5.6
range adaptors execution trace - output
// ev:0
// sq:0
// for: 0
// ev:1
// ev:2
// sq:2
// for: 4
// ev:3
// ev:4
// sq:4
// for: 16
// ev:5
for: <- 3
square <- 3
even <- 6
5.7
Types in a view chain - under the hood
//typename magic
template <class T>
constexpr
std::string_view
type_name()
{
//gcc only version see https://stackoverflow.com/questions/81870/
std::string_view p = __PRETTY_FUNCTION__;
return std::string_view(p.data() + 49, p.find(';', 49) - 49);
}
5.8
Types in a view chain - under the hood
vector<string> vs{"hello", " ", "ranges", "!"};
auto jv = join_view{vs};
cout << type_name<decltype(jv)>() << "\n";
//ranges::join_view<ranges::ref_view<vector<basic_string<char> > > >
https://godbolt.org/z/G5PE614ff
5.9
views and const iteration
perhaps suprisingly shouldn't pass by const&
why? views like filter_view cache
prefer to pass by value or forwarding reference
https://quuxplusone.github.io/blog/2023/08/13/non-
const-iterable-ranges/
template<std::ranges::range R>
void doit( const R& range); // nope
template<std::ranges::range R>
void doit( R&& range); // yes!
5 . 10
composition: views and range algorithm
example
//utility functions
auto print_int = [](int i) { cout << i << " "; };
auto is_even = [](int i) { return i % 2 == 0; };
vector<int> v { 6, 2, 3, 4, 5, 6 };
https://godbolt.org/z/c137bM8f4
5 . 11
Survey of Views - by example
6.1
cheatsheet
6.2
cartesian_product example
(23)
/* output
0 2
0 3
1 2
1 3
*/
namespace rv = ranges::views;
std::vector<int> v1 { 0, 1 };
std::vector<int> v2 { 2, 3 };
for (auto&& [a,b] : rv::cartesian_product(v1, v2))
{
std::print("{} {}\n", a, b);
}
https://godbolt.org/z/rYehsTMxY 6.3
chunk_by (23) example
std::vector v = {1, 2, 2, 3, 0, 4, 5, 2};
// [[1, 2, 2, 3], [0, 4, 5], [2]]
std::print("{}\n", v | std::views::chunk_by( ranges::less_equal{} ));
6.4
zip and zip_transform (23)
bind multiple ranges with pairwise matching
especially important for dealing tuple and pair
6.5
zip example
std::vector v1 = {1, 2};
std::vector v2 = {'a', 'b', 'c'};
std::vector v3 = {3, 4, 5};
6.6
Adjuncts
7.1
span
span is a non-owning 'view' over contiguous
sequence of object
cheap to copy - implementation is a pointer and size
constant time complexity for all member functions
de ned in header <span>
unlike most 'view types' can mutate
constexpr ready
7.2
span construction
vector<int> vi = { 1, 2, 3, 4, 5 };
span<int> si ( vi );
array<int, 5> ai = { 1, 2, 3, 4, 5 };
span<int> si2 ( ai );
int cai[] = { 1, 2, 3, 4, 5 };
span<int> si3( cai );
7.3
span as a function parameter
void print_reverse(span<int> si) { //by value
for ( auto i : ranges::reverse_view{si} ) {
cout << i << " " ;
}
cout << "\n";
}
int main () {
vector<int> vi = { 1, 2, 3, 4, 5 };
print_reverse( vi ); //5 4 3 2 1
span<int> si ( vi );
print_reverse( si.first(2) ); //2 1
print_reverse( si.last(2) ); //5 4
7.4
ranges::to
std::vector<int> numbers = {1, 2, 3, 4, 5};
https://godbolt.org/z/c137bM8f4
7.5
ranges::to string example
namespace rv = ranges::views;
s = vs | rv::filter(is_not_empty)
| rv::join('-')
| ranges::to<string>();
cout << s << "\n"; //foo-bar-baz
https://godbolt.org/z/bd558h47j
7.6
range constructors and other
operations
collections and string gain range constructors
insert_range, assign_range, append_range,
prepend_range
https://wg21.link/P1206
template<container-compatible-range<T> R>
constexpr vector(from_range_t, R&& rg);
7.7
writing a view
write your own
make it pipeable
takes a lot of boiler plate
https://www.boost.org/doc/libs/1_83_0/doc/html/stl_i
7.8
Observations and Future
8.1
sum of squares
auto square = [](int x) {return x*x;};
auto add = [](int x, int y) {return x+y;};
// [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)]
print("{}\n", square_view | rv::enumerate );
https://godbolt.org/z/qPh1hjdzo
8.2
C++26
algos: reduce, sum, product
views: concat, cycle, getlines
views: drop_last, take_last, drop_exactly,
take_exactly
views: split_when
views: remove, remove_if, replace,
replace_if
views: transform_join
and a lot more…
https://wg21.link/P2214 8.3
advice
always use range algorithms rst
constrained, better errors
projections, superior results
view adapters rst
don't be evil and write inscrutable pipelines
8.4
nal thoughts
Ranges is key building blocks for the future
overall: nicer cleaner more capable code
begin ing not an end
8.5
Finish
ranges::single_view ssv{" Thank You!! \n"s};
for (string s : ssv) cout << s;
8.6