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

C++23: How to erase a subrange from vector


I'm trying to erase a subrange returned by find_if( vec | reverse | slide(width) ) from a vector, but I can't get their parameter types to match anyway.

I'm not sure if the iterator is invalidated in these pipes. I'd like to just use the existing subrange iterator to erase it instead of wasting performance to find it again. Is that possible?


Full code: Compiler Explorer

Core function:

std::vector<Data> take_last_successive_data(std::vector<Data> &target) {
    const auto &data_is_successive = [](const auto &data) { return data.is_successive; };

    auto windows = target | views::reverse | views::slide(2);
    auto result = ranges::find_if(
        windows,
        [&](const auto &window) {
            return ranges::all_of(window | views::take(window.size() - 1), data_is_successive);
        }
    );

    if (result == windows.end()) {
        std::cout << "not found\n";
        return {};
    } else {
        // How to erase subrange `result`
        target.erase();

        auto ret = to_vector(*result | views::reverse);
        for (const auto &data : ret) {
            std::cout << data.value << ", ";
        }
        std::cout << '\n';
        return ret;
    }
}

Solution

  • First, decompose subrange to get the pair of reverse_iterator, then call its base() to get the underlying iterator and pass it into erase().

    Note that the erase() must be performed after ret is created to ensure that the iterator points to a valid element.

    } else {
        auto ret = to_vector(*result | views::reverse);
        for (const auto &data : ret) {
            std::cout << data.value << ", ";
        }
        std::cout << '\n';
    
        // How to erase subrange `result`
        auto [rbegin, rend] = *result;
        target.erase(rend.base(), rbegin.base());
        
        return ret;
    }
    

    Demo