C++20: The Ranges Library

Contents[Show]

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.

 

TimelineCpp20

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.

 

Rainer D 6 P2 540x540Modernes C++ Mentoring

Be part of my mentoring programs:

 

 

 

 

Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.

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:

RangeConcepts

 

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.

CompilerExplorer

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:Wandbox

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, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, 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, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.

 

Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

My special thanks to Tipi.build tipi.build logo

 

My special thanks to Take Up code TakeUpCode 450 60

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)

German

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++

New

  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++,

RainerGrimmDunkelBlauSmall

 

Tags: Ranges

Stay Informed about my Mentoring

 

Mentoring

English Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Course: The All-in-One Guide to C++20

Course: Master Software Design Patterns and Architecture in C++

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code

Visitors

Today 3783

Yesterday 4371

Week 39590

Month 169715

All 12057481

Currently are 162 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments