-
Book Overview & Buying
-
Table Of Contents
C++ STL Cookbook - Second Edition
By :
Beginning with C++23, we can now initialize a container directly from a range or view.
Until now, to initialize a container from a range we could use a for loop:
vector<int> v {};
for (auto n : views::iota(1, 11)) {
v.push_back(n);
}
Or we could construct the vector from the range iterators:
auto r = views::iota(1, 11);
vector<int> v(r.begin(), r.end());
In C++23, this becomes much easier with the new ranges::to class:
auto v = views::iota(1, 11) | ranges::to<vector<int>>();
This new pattern is both simpler and clearer. The ranges::to class is defined in the <ranges> header.
The ranges::to class takes any range or view as input via the pipe syntax and returns a new non-view object.
ranges and views:
namespace ranges = std::ranges;
namespace views = ranges::views;
vector from a view:
auto vec = views::iota(1, 11)
| views::transform([](const auto n){ return n * 5; })
| ranges::to<vector>();
for (auto n : vec) print("{} ", n);
print("\n");
In this example, views::iota(1, 11) generates a sequence of integers from 1 to 10. We then use views::transform with a lambda function to multiply each integer by 5. Finally, ranges::to<vector>() returns a new vector for our initialization.
The function call operator (parentheses) after ranges::to<> is required for use with the pipe syntax:
auto vec = r | ranges::to<vector>; // syntax error
auto vec = r | ranges::to<vector>(); // correct usage
This produces the output:
5 10 15 20 25 30 35 40 45 50
auto lst = vec | views::reverse
| ranges::to<list<double>>();
for (auto d : lst) print("{} ", d);
print("\n");
In this example, we use views::reverse to reverse the order of the sequence, and ranges::to<list<double>>() to construct a list of double, which produces the output:
50 45 40 35 30 25 20 15 10 5
Let's take a look at how this works.
The ranges::to class is designed to work in a variety of contexts. To return a container C from range R, it considers a series of scenarios and chooses the first valid method from the list:
C from RC from R using a tagged range constructor (from_range_t)C from the range iterators R.begin(), R.end()C, then insert each element of R at the end of CC is a range whose value type is itself a range (and is not a view), and R's value type is also a range, for each element of R, a value of to<range_value_t> is inserted at the end of CThis allows a more unified and concise syntax for constructing containers from ranges.