Search code examples
c++c++20std-rangesc++23

What is the best way to drop last element using c++20 ranges


Is there any better way to drop last element in container using c++20 ranges than reverse it twice?

#include <iostream>
#include <vector>
#include <ranges>

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

    for (const auto& d: foo | std::ranges::views::reverse 
                            | std::ranges::views::drop(1) 
                            | std::ranges::views::reverse)
    {
        std::cout << d << std::endl;
    }
}

Solution

  • What you need is views::drop_last which comes from p2214 and has a priority of Tier 2.

    As the paper says:

    We’ll go through the other potential range adapters in this family and discuss how they could be implemented in terms of existing adapters:

    • take_last(N) and drop_last(N). views::take_last(N) is equivalent to views::reverse | views::take(N) | views::reverse. But this is somewhat expensive, especially for non-common views. For random-access, sized ranges, we’re probably want r | views::take_last(N) to evaluate as r | views::drop(r.size() - N), and that desire is really the crux of this whole question — is the equivalent version good enough or should we want to do it right?

    Since vector is a random-access, sized range, you can just do

    for (const auto& d: foo | std::views::take(foo.size() - 1))
    {
        std::cout << d << std::endl;
    }