C++20: The Ranges Library
Thanks to the ranges library in C++20, working with the Standard Template Library (STL) will become much more comfortable and powerful. The algorithms of the ranges library are lazy, can work directly on the container and can easily be composed. To make it short: The range library’s comfort and power are due to its functional ideas. Let me show you what that means.
Before I dive into the details, here is a first example of the ranges library:
// rangesFilterTransform.cpp #include <iostream> #include <ranges> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5, 6}; auto results = numbers | std::views::filter([](int n){ return n % 2 == 0; }) | std::views::transform([](int n){ return n * 2; }); for (auto v: results) std::cout << v << " "; // 4 8 12 }
You have to read the expression from left to right. The pipe symbol stands for function composition: First, all numbers can pass which are even (std::views::filter([](int n){ return n % 2 == 0; })). Afterward, each remaining number is mapped to its double (std::views::transform([](int n){ return n * 2; })). The small example already shows two new ranges library features: Function composition, which operators on the entire container.
Now you should be prepared for the details. Let’s go back to square one: ranges and views are concepts.
Range
- std::range: A range is a group of items you can iterator over. It provides a begin iterator and an end sentinel. Of course, the containers of the STL are ranges.
There exist refinements of std::range:
- std::ranges::input_range: specifies a range whose iterator type satisfies input_iterator (can iterate from beginning to end at least once)
- std::ranges::output_range: specifies a range whose iterator type satisfies output_iterator
- std::ranges::forward_range: specifies a range whose iterator type satisfies forward_iterator (can iterate from beginning to end more than once)
- std::ranges::bidirectional_range: specifies a range whose iterator type satisfies bidirectional_iterator (can iterate forward and backward more than once)
- std::ranges::random_access_range: specifies a range whose iterator type satisfies random_access_iterator (can jump in constant time to an arbitrary element with the index operator [])
- std::ranges::contiguous_range: specifies a range whose iterator type satisfies contiguous_iterator (elements are stored consecutively in memory)
The containers of the STL and std::string model different concepts:
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
A container supporting the std::ranges::contiguous_range concept supports all other concepts above, such as std::ranges::random_access_range, std::ranges::bidirectional_range, and std::ranges::input_range. The same observation holds for all other ranges.
View
- You apply a View on a range and perform some operations. A view does not own data, and it’s time to copy, move, assignment its constant. Here is a quote from Eric Niebler’s range-v3 implementation, which is the base for the C++20 ranges: “Views are composable adaptations of ranges where the adaptation happens lazily as the view is iterated.“
std::vector<int> numbers = {1, 2, 3, 4, 5, 6}; auto results = numbers | std::views::filter([](int n){ return n % 2 == 0; }) | std::views::transform([](int n){ return n * 2; });
In this code snippet, numbers are the range, and std::views::filter and std::views::transform are the views.
Due to the power of views, C++20 allows programming in a functional style. Views can be combined and are lazy. l already presented two views, but we get more.
std::all_view, std::views::all // takes all elements std::ref_view // takes all elements of another view std::filter_view, std::views::filter // takes the elements which satisfies the predicate std::transform_view, std::views::transform // transforms each element std::take_view, std::views::take // takes the first N elements of another view std::take_while_view, std::views::take_while // takes the elements of another view as long as the predicate returns true std::drop_view, std::views::drop // skips the first N elements of another view std::drop_while_view, std::views::drop_while // skips the initial elements of another view until the predicate returns false std::join_view, std::views::join // joins a view of ranges std::split_view, std::views::split // splits a view by using a delimiter std::common_view, std::views::common // converts a view into a std::common_range std::reverse_view, std::views::reverse // iterates in reverse order std::basic_istream_view, std::istream_view // applies operator>> on the view std::elements_view, std::views::elements // creates a view on the N-th element of tuples std::keys_view, std::views::keys // creates a view on the first element of a pair-like values std::values_view, std::views::values // creates a view on the second elements of a pair-like values
You can generally use a view such as std::views::transform with the alternative name std::transform_view. I show the usage of various views as I go on.
Implementation Status
As far as I know, there is no implementation of the ranges library now (February 2020) available. This is not an issue. You can use the mentioned range-v3 implementation on the online Wandbox or the Compiler Explorer with the HEAD GCC. Here is what you have to do to translate my examples, such as rangesFilterTransform.cpp to see it in action.
- Replace the namespaces std::views:: with ranges::views::.
- Replace the header <ranges> with the header <range/v3/all.hpp>. For more details, study the documentation in range-v3 implementation.
- Compile your program with C++20 support:
-std=c++2a.
- When you use Compiler Explorer, you have to use the trunk version of the range-v3 implementation. The following picture should help you find the option.
Transforming the program get rangesFilterTransform.cpp gives me the following program.
// rangesV3FilterTransform.cpp #include <iostream> #include <range/v3/all.hpp> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5, 6}; auto results = numbers | ranges::views::filter([](int n){ return n % 2 == 0; }) | ranges::views::transform([](int n){ return n * 2; }); for (auto v: results) std::cout << v << " "; }
Thanks to Wandbox, here is the output of the program without a faked source file:
I will use in my future posts the proposed C++20 syntax. Consequentially, you have to do the transformation step manually.
What’s next?
In this post, I explained the basics of the ranges library. These basics enable me to write in my next post about their power. The ranges library extends C++20 with two new concepts: function composition and lazy evaluation. This is the reason ranges belong to the big four of C++20. Each part of the big four changes how we think and write modern C++.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery,and Matt Godbolt.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Seminars
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ – The Core Language
- C++ – The Standard Library
- C++ – Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
- Clean Code with Modern C++
- C++20
Contact Me
- Mobil: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,
Leave a Reply
Want to join the discussion?Feel free to contribute!