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 comfort and the power of the ranges library 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; })). Afterwards, each remaining number is mapped to its double (std::views::transform([](int n){ return n * 2; })). The small example shows already two new features of the ranges library: 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 that 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 begin 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 begin 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

  • A View is something that you apply on a range and performs some operation. A view does not own data and it's time to copy, move, assignment its constant. Here is a quote from Eric Nieblers 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 is 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

 

In general, you can 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 already mentioned  range-v3 implementation on the online compiler 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 the 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 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 to the ranges library. These basics enables 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 the way we think, and we write modern C++.

 

 

Thanks a lot to my Patreon Supporters: Meeting C++, Matt Braun, Roman Postanciuc, Venkata Ramesh Gudpati, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Richard Ohnemus, Frank Grimm, Sakib, Broeserl, António Pina, Markus Falkner, Darshan Mody, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, and Animus24.

 

Thanks in particular to:   crp4

 

   

Get your e-book at Leanpub:

The C++ Standard Library

 

Concurrency With Modern C++

 

Get Both as one Bundle

cover   ConcurrencyCoverFrame   bundle
With C++11, C++14, and C++17 we got a lot of new C++ libraries. In addition, the existing ones are greatly improved. The key idea of my book is to give you the necessary information to the current C++ libraries in about 200 pages. I also included more than 120 source files.  

C++11 is the first C++ standard that deals with concurrency. The story goes on with C++17 and will continue with C++20.

I'll give you a detailed insight in the current and the upcoming concurrency in C++. This insight includes the theory and a lot of practice with more than 140 source files.

 

Get my books "The C++ Standard Library" (including C++17) and "Concurrency with Modern C++" in a bundle.

In sum, you get more than 700 pages full of modern C++ and more than 260 source files presenting concurrency in practice.

 

Get your interactive course

 

Modern C++ Concurrency in Practice

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

educative CLibrary

Based on my book "Concurrency with Modern C++" educative.io created an interactive course.

What's Inside?

  • 140 lessons
  • 110 code playgrounds => Runs in the browser
  • 78 code snippets
  • 55 illustrations

Based on my book "The C++ Standard Library" educative.io created an interactive course.

What's Inside?

  • 149 lessons
  • 111 code playgrounds => Runs in the browser
  • 164 code snippets
  • 25 illustrations

My Newest E-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

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 3149

All 3929692

Currently are 223 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments