C++ Ranges — Part 2
The input vector:
std::vector vec{ 1, 2, 3, 4, 5, 6 };
A key feature of views is that whatever transformation they apply, they do so at the moment you request an element, not when the view is created.
auto v1 = std::views::reverse( vec );
Here v1
is a view; creating it neither changes vec, nor does
v1
store any elements. The time it takes to construct v1
and its size in memory is independent of the size of vec
.
std::cout << "v1.begin = " << *v1.begin() << '\n';
std::cout << "vec.rbegin = " << *vec.rbegin() << '\n';
This will print 6
, but the important thing is that resolving
the first element of v to the last element of vec happens
on-demand. This guarantees that views can be used as flexibly
as iterators, but it also means that if the view performs an
expensive transformation, it will have to do so repeatedly if
the same element is requested multiple times.
Changes on vector reflect on views
vec[5] = 60;
std::cout << "v1.begin = " << *v1.begin() << '\n';
std::cout << "vec.rbegin = " << *vec.rbegin() << '\n';
std::views::reverse
is not the view itself, it’s an adaptor that
takes the underlying range (in our case the vector) and returns a
view object over the vector. The exact type of this view is hidden
behind the auto statement. This has the advantage, that we don’t
need to worry about the template arguments of the view type, but
more importantly the adaptor has an additional feature: it can be
chained with other adaptors!
auto v2 = vec | std::views::reverse | std::views::drop( 2 );
std::cout << "v2.begin() = " << *v2.begin() << '\n';
It will print 4
, because 4
is the 0-th element of the reversed
string after dropping the first two.
vec | foo | bar(3) | baz(7)
is equivalent to baz(bar(foo(vec), 3), 7)
auto v3 = vec //
| std::views::filter( []( auto const i ) { return i % 2 == 0; } )
| std::views::transform( []( auto const i ) { return i * i; } );
std::cout << "v3.begin() = " << *v3.begin() << '\n'; // prints 4
vec[1] = 16; // 2 -> 16
std::cout << "v3.begin() = " << *v3.begin() << '\n'; // prints 256
Views are not readonly
auto v4 = vec | std::views::reverse | std::views::drop( 2 );
*v4.begin() = 42; // now vec == {1, 2, 3, 42, 5, 6 } !!
std::cout << "vec[3] = " << vec[3] << '\n';
Output
v1.begin = 6
vec.rbegin = 6
v1.begin = 60
vec.rbegin = 60
v2.begin() = 4
v3.begin() = 4
v3.begin() = 256
vec[3] = 42