The Ranges library proposal has been accepted for C++20 at the San Diego meeting of the standard committee in November last year. The library provides components for handling ranges of values aimed at simplifying our code. Unfortunately, the Ranges library is not very well documented, which makes it harder to grasp for those that want to learn it. This post is intended as an introduction based on examples of code written with and without Ranges.

Eric Niebler’s implementation of the Ranges library is available here. It works will Clang 3.6.2 or later, gcc 5.2 or later, and VC++ 15.9 or later. The code samples below were written and tested with the latter. On a side note, these samples represent typical implementations and not necessarily the only solutions one could think of.

Although the standard namespace for the Ranges library is std::ranges , in this current implementation of the library it is ranges::v3 .

The following namespace aliases are used in the samples below:

namespace rs = ranges::v3; namespace rv = ranges::v3::view; namespace ra = ranges::v3::action; 1 2 3 namespace rs = ranges : : v3 ; namespace rv = ranges : : v3 : : view ; namespace ra = ranges : : v3 : : action ;

Also, for simplicity, we will refer to the following object, function, and lambda functions:

std::string to_roman(int value) { std::vector<std::pair<int, char const*>> roman { { 1000, "M" },{ 900, "CM" }, { 500, "D" },{ 400, "CD" }, { 100, "C" },{ 90, "XC" }, { 50, "L" },{ 40, "XL" }, { 10, "X" },{ 9, "IX" }, { 5, "V" },{ 4, "IV" }, { 1, "I" } }; std::string result; for (auto const & [d, r]: roman) { while (value >= d) { result += r; value -= d; } } return result; } std::vector<int> v{1,1,2,3,5,8,13,21,34}; auto print_elem = [](auto const e) {std::cout << e << '

'; }; auto is_even = [](auto const i) {return i % 2 == 0; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 std : : string to_roman ( int value ) { std : : vector < std : : pair < int , char const * > > roman { { 1000 , "M" } , { 900 , "CM" } , { 500 , "D" } , { 400 , "CD" } , { 100 , "C" } , { 90 , "XC" } , { 50 , "L" } , { 40 , "XL" } , { 10 , "X" } , { 9 , "IX" } , { 5 , "V" } , { 4 , "IV" } , { 1 , "I" } } ; std : : string result ; for ( auto const & [d, r]: roman) { while (value >= d) { result += r; value -= d ; } } return result ; } std : : vector < int > v { 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 } ; auto print_elem = [ ] ( auto const e ) { std : : cout < < e < < '

' ; } ; auto is_even = [ ] ( auto const i ) { return i % 2 == 0 ; } ;

UPDATE: I would like to thank Eric Niebler and all the others that commented below with suggestions for these code samples. I have updated a few based on their feedback.

Print the all the elements of a range:

Before Ranges After Ranges std::for_each( std::cbegin(v), std::cend(v), print_elem); // or for(auto const i : v) { print_elem(i); }; 1 2 3 4 5 6 7 8 9 10 std : : for_each ( std : : cbegin ( v ) , std : : cend ( v ) , print_elem ) ; // or for ( auto const i : v ) { print_elem ( i ) ; } ; rs::for_each( std::cbegin(v), std::cend(v), print_elem); // or rs::for_each(std::as_const(v), print_elem); 1 2 3 4 5 6 7 rs : : for_each ( std : : cbegin ( v ) , std : : cend ( v ) , print_elem ) ; // or rs : : for_each ( std : : as_const ( v ) , print_elem ) ;

Print all the elements of a range in reverse order:

Before Ranges After Ranges std::for_each( std::crbegin(v), std::crend(v), print_elem); 1 2 3 std : : for_each ( std : : crbegin ( v ) , std : : crend ( v ) , print_elem ) ; rs::for_each( std::crbegin(v), std::crend(v), print_elem); // or for (auto const i : v | rv::reverse) { print_elem(i); }; 1 2 3 4 5 6 7 8 9 10 rs : : for_each ( std : : crbegin ( v ) , std : : crend ( v ) , print_elem ) ; // or for ( auto const i : v | rv : : reverse ) { print_elem ( i ) ; } ;

Print only the even elements of the range but in reverse order:

Before Ranges After Ranges std::for_each( std::crbegin(v), std::crend(v), [print_elem](auto const i) { if(i % 2 == 0) print_elem(i); }); 1 2 3 4 5 6 std : : for_each ( std : : crbegin ( v ) , std : : crend ( v ) , [ print_elem ] ( auto const i ) { if ( i % 2 == 0 ) print_elem ( i ) ; } ) ; for (auto const i : v | rv::reverse | rv::filter(is_even)) { print_elem(i); }; 1 2 3 4 5 6 for ( auto const i : v | rv : : reverse | rv : : filter ( is_even ) ) { print_elem ( i ) ; } ;

Skip the first two elements of the range and print only the even numbers of the next three in the range:

Before Ranges After Ranges auto it = std::cbegin(v); std::advance(it, 2); auto ix = 0; while (it != std::cend(v) && ix++ < 3) { if (is_even(*it)) print_elem(*it); it++; } 1 2 3 4 5 6 7 8 9 auto it = std : : cbegin ( v ) ; std : : advance ( it , 2 ) ; auto ix = 0 ; while ( it ! = std : : cend ( v ) && ix++ < 3) { if (is_even(*it)) print_elem(*it); it ++ ; } for (auto const i : v | rv::drop(2) | rv::take(3) | rv::filter(is_even)) { print_elem(i); }; 1 2 3 4 5 6 7 for ( auto const i : v | rv : : drop ( 2 ) | rv : : take ( 3 ) | rv : : filter ( is_even ) ) { print_elem ( i ) ; } ;

Print all numbers from 101 to 200:

Before Ranges After Ranges for (int n = 101; n <= 200; ++n) { print_elem(n); } 1 2 3 4 for ( int n = 101 ; n < = 200 ; ++ n ) { print_elem ( n ) ; } for (auto n : rs::iota_view(101, 201)) { print_elem(n); } 1 2 3 4 for ( auto n : rs : : iota_view ( 101 , 201 ) ) { print_elem ( n ) ; }

Print all the Roman numerals from 101 to 200. To convert a number to its corresponding Roman numeral the function to_roman() shown ealier is used.

Before Ranges After Ranges for (int i = 101; i <= 200; ++i) { print_elem(to_roman(i)); } 1 2 3 4 for ( int i = 101 ; i < = 200 ; ++ i ) { print_elem ( to_roman ( i ) ) ; } for (auto n : rs::iota_view(101, 201) | rv::transform(to_roman)) { print_elem(n); } // or rs::for_each(rv::iota(101, 201), print_element, to_roman); 1 2 3 4 5 6 7 8 9 10 for ( auto n : rs : : iota_view ( 101 , 201 ) | rv : : transform ( to_roman ) ) { print_elem ( n ) ; } // or rs : : for_each ( rv : : iota ( 101 , 201 ) , print_element , to_roman ) ;

Print the Roman numeral of the last three numbers divisible to 7 in the range [101, 200], in reverse order.

Before Ranges After Ranges for (int n = 200, count=0; n >= 101 && count < 3; --n) { if (n % 7 == 0) { print_elem(to_roman(n)); count++; } } 1 2 3 4 5 6 7 8 for ( int n = 200 , count = 0 ; n > = 101 && count < 3; -- n ) { if ( n % 7 == 0 ) { print_elem ( to_roman ( n ) ) ; count ++ ; } } for (auto n : rs::iota_view(101, 201) | rv::reverse | rv::filter([](auto v) { return v % 7 == 0; }) | rv::transform(to_roman) | rv::take(3)) { print_elem(n); } 1 2 3 4 5 6 7 8 9 for ( auto n : rs : : iota_view ( 101 , 201 ) | rv : : reverse | rv : : filter ( [ ] ( auto v ) { return v % 7 == 0 ; } ) | rv : : transform ( to_roman ) | rv : : take ( 3 ) ) { print_elem ( n ) ; }

Create a range of strings containing the Roman numeral of the last three numbers divisible to 7 in the range [101, 200], in reverse order.

Before Ranges After Ranges std::vector<std::string> v; for (int n = 200, count = 0; n >= 101 && count < 3; --n) { if (n % 7 == 0) { v.push_back(to_roman(n)); count++; } } 1 2 3 4 5 6 7 8 9 10 std : : vector < std : : string > v ; for ( int n = 200 , count = 0 ; n > = 101 && count < 3; -- n ) { if ( n % 7 == 0 ) { v . push_back ( to_roman ( n ) ) ; count ++ ; } } auto v = rs::iota_view(101, 201) | rv::reverse | rv::filter([](auto v) {return v % 7 == 0; }) | rv::transform(to_roman) | rv::take(3) | rs::to_vector; 1 2 3 4 5 6 auto v = rs : : iota_view ( 101 , 201 ) | rv : : reverse | rv : : filter ( [ ] ( auto v ) { return v % 7 == 0 ; } ) | rv : : transform ( to_roman ) | rv : : take ( 3 ) | rs : : to_vector ;

Modify an unsorted range so that it retains only the unique values but in reverse order.

Before Ranges After Ranges std::vector<int> v{ 21, 1, 3, 8, 13, 1, 5, 2 }; std::sort(std::begin(v), std::end(v)); v.erase( std::unique(std::begin(v), std::end(v)), std::end(v)); std::reverse(std::begin(v), std::end(v)); 1 2 3 4 5 6 7 std : : vector < int > v { 21 , 1 , 3 , 8 , 13 , 1 , 5 , 2 } ; std : : sort ( std : : begin ( v ) , std : : end ( v ) ) ; v . erase ( std : : unique ( std : : begin ( v ) , std : : end ( v ) ) , std : : end ( v ) ) ; std : : reverse ( std : : begin ( v ) , std : : end ( v ) ) ; std::vector<int> v{ 21, 1, 3, 8, 13, 1, 5, 2 }; v = std::move(v) | ra::sort | ra::unique | ra::reverse; 1 2 3 4 5 6 std : : vector < int > v { 21 , 1 , 3 , 8 , 13 , 1 , 5 , 2 } ; v = std : : move ( v ) | ra : : sort | ra : : unique | ra : : reverse ;

Remove the smallest two and the largest two values of a range and retain the other ones, ordered, in a second range.

Before Ranges After Ranges std::vector<int> v{ 21, 1, 3, 8, 13, 1, 5, 2 }; std::vector<int> v2 = v; std::sort(std::begin(v2), std::end(v2)); auto first = std::begin(v2); std::advance(first, 2); auto last = first; std::advance(last, std::size(v2) - 4); v2.erase(last, std::end(v2)); v2.erase(std::begin(v2), first); 1 2 3 4 5 6 7 8 9 10 11 std : : vector < int > v { 21 , 1 , 3 , 8 , 13 , 1 , 5 , 2 } ; std : : vector < int > v2 = v ; std : : sort ( std : : begin ( v2 ) , std : : end ( v2 ) ) ; auto first = std : : begin ( v2 ) ; std : : advance ( first , 2 ) ; auto last = first ; std : : advance ( last , std : : size ( v2 ) - 4 ) ; v2 . erase ( last , std : : end ( v2 ) ) ; v2 . erase ( std : : begin ( v2 ) , first ) ; std::vector<int> v{ 21, 1, 3, 8, 13, 1, 5, 2 }; auto v2 = v | rs::copy | ra::sort | ra::slice(2, rs::end - 2); 1 2 3 4 5 std : : vector < int > v { 21 , 1 , 3 , 8 , 13 , 1 , 5 , 2 } ; auto v2 = v | rs : : copy | ra : : sort | ra : : slice ( 2 , rs : : end - 2 ) ;

Concatenate all the strings in a given range into a single value.

Before Ranges After Ranges std::vector<std::string> words { "Lorem", " ", "ipsum", " ", "dolor", " ", "sit", " ", "amet"}; std::string text; for (auto const & word : words) text += word; 1 2 3 4 5 6 7 8 std : : vector < std : : string > words { "Lorem" , " " , "ipsum" , " " , "dolor" , " " , "sit" , " " , "amet" } ; std : : string text ; for ( auto const & word : words) text += word; std::vector<std::string> words { "Lorem", " ", "ipsum", " ", "dolor", " ", "sit", " ", "amet"}; std::string text = words | rs::move | ra::join; 1 2 3 4 5 6 7 8 std : : vector < std : : string > words { "Lorem" , " " , "ipsum" , " " , "dolor" , " " , "sit" , " " , "amet" } ; std : : string text = words | rs : : move | ra : : join ;

Count the number of words (as delimited by space) in a text.

Before Ranges After Ranges auto text = "Lorem ipsum dolor sit amet"; std::istringstream iss(text); std::vector<std::string> words( std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>()); auto count = words.size(); // or size_t count = 0; std::vector<std::string> words; std::string token; std::istringstream tokenStream(text); while (std::getline(tokenStream, token, ' ')) { ++count; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 auto text = "Lorem ipsum dolor sit amet" ; std : : istringstream iss ( text ) ; std : : vector < std : : string > words ( std : : istream_iterator < std : : string > { iss } , std : : istream_iterator < std : : string > ( ) ) ; auto count = words . size ( ) ; // or size_t count = 0 ; std : : vector < std : : string > words ; std : : string token ; std : : istringstream tokenStream ( text ) ; while ( std : : getline ( tokenStream , token , ' ' ) ) { ++ count ; } auto text = "Lorem ipsum dolor sit amet"; auto count = rs::distance( rv::c_str(text) | rv::split(' ')); 1 2 3 4 auto text = "Lorem ipsum dolor sit amet" ; auto count = rs : : distance ( rv : : c_str ( text ) | rv : : split ( ' ' ) ) ;

Share this: Facebook

Twitter

Print

More

Email

Reddit



