Search code examples
c++range-v3

What is the difference between range-v3 views::drop and views::drop_exactly?


Can someone explain the difference between range-v3's view adaptors drop and drop_exactly?

One difference I've observed is that if the number of elements in the range that is piped to these views is less than the argument to the view adaptors, drop seems to do the right thing, while drop_exactly seems to invoke UB.

When the argument is less than the number of elements in the range that is piped to these views, they both seem to work the same:

#include <iostream>
#include <vector>
#include <range/v3/all.hpp>

namespace rv = ranges::views;

int main() 
{    
    std::vector<int> v { 1, 2, 3, 4, 5};

    for (int i : v | rv::drop(3))
        std::cout << i;                    // prints 45
    
    for (int i : v | rv::drop(7))
        std::cout << i;                    // prints nothing

    for (int i : v | rv::drop_exactly(3))
        std::cout << i;                    // prints 45
    
    for (int i : v | rv::drop_exactly(7))
        std::cout << i;                    // prints garbage and crashes
}

Here's the code.


Solution

  • From the documentation for drop_exactly:

    Given a source range and an integral count, return a range consisting of all but the first count elements from the source range. The source range must have at least that many elements.

    While the documentation for drop states:

    Given a source range and an integral count, return a range consisting of all but the first count elements from the source range, or an empty range if it has fewer elements.

    emphasis added


    I'm guessing that drop_exactly avoids bounds checks and therefore has the potential to be slightly more performant at the cost of maybe running past the end of the piped-in container, while drop apparently performs bounds checks to make sure you don't.

    This is consistent with what you see. If you print stuff from begin()+7 up to begin()+5 (aka end()) of a std::vector, and the abort condition is implemented with != instead of <, then you will continue to print the junk data that sits in the space allocated by the vector until at some point you run over the allocated chunk and the operating system steps in and segfaults your binary.

    So, if you know the container to have as many entries as you wish to drop use the faster drop_exactly, otherwise use drop.