C++ code samples before and after Ranges

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:

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

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

Print all the elements of a range in reverse order:

Before Ranges After Ranges

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

Before Ranges After Ranges

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

Print all numbers from 101 to 200:

Before Ranges After Ranges

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

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

Before Ranges After Ranges

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

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

Before Ranges After Ranges

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

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

Before Ranges After Ranges

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

Before Ranges After Ranges

15 Replies to “C++ code samples before and after Ranges”

  1. Thanks Marius!
    Just a quick one: the string concatenation example can be done with accumulate too (in both non-ranges and ranges versions).

  2. I like your namespace aliases! Are they in common use or is everyone making up their own?

    On the very last example: surely the “pre-Ranges” version would use ++count in the loop, instead of populating a whole heap-allocated vector of heap-allocated strings purely in order to take its .size(). What would be the equivalent of that in the “post-Ranges” version, or does what’s there now already avoid materializing the range?

  3. I made up the namespace aliases; I do not know if others are using the same I have not seen lots of Ranges code.

    Yes, you are right about the last sample. Should you just want to count the words there is no need to populate a vector with them. Point taken, thank you.

  4. Wouldn’t it be more idiomatic to have the following for “Print the all the elements of a range”
    rs::for_each(std::as_const(v), print_elem);

  5. … but nice work apart from that. Oh, and the first example could be simplified to rs::for_each(std::as_const(v),print_elem). And look into projections. For instance, instead of:


    for (auto n : rs::iota_view(101, 201)
    | rv::transform(to_roman))
    {
    print_elem(n);
    }

    you can write:


    rs::for_each(rv::iota(101, 201), print_element, to_roman);

  6. You might want to use a reference in the parameters of print_elem and is_even. By the way what is wrong with not using as_const when passing v to for_each?

  7. Awesome post!

    I was totally convinced at this example: “Print the Roman numeral of the last three numbers divisible to 7 in the range [101, 200], in reverse order.”
    Here, I believe that the library beats even English! Really, the code is more readable that the above statement!

    Also at some places we are doing this:

    | rv::transform(to_roman)
    | rv::take(3)

    How about the following code?


    | rv::take(3)
    | rv::transform(to_roman)

    Will this be more efficient? Or does the library handles it in a way that it doesn’t have to apply to_roman() to all the elements, but just the 3?

  8. Surely “print the Roman numeral of the last three numbers divisible to 7 in the range [101, 200], in reverse order” can be done with a simple programmer-time optimisation:

    cout << "CXCVI\nCLXXXIX\nCLXXXII\n";

    🙂

  9. All the nice stuff with ranges come with a serious caveat: you will be falling asleep waiting for your programs to finish compiling :-/

  10. Thanks for your great blog. Are include file and std::ranges available with either or both of the following compilers?
    Clang++ 8.0.0 or GCC’s g++ 9.1.0 compilers?

    If not any other C++ compiler(s)? And link(s) for downloading?

    Thanks

  11. In my previous comment, I meant to type Are include ranges file and std::ranges available …. ?
    Thanks

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.