template <typename Items_it, typename Indices_it> auto remove_indices( Items_it items_begin, Items_it items_end , Indices_it indices_begin, Indices_it indices_end ) { static_assert( std::is_same_v<std::random_access_iterator_tag , typename std::iterator_traits<Items_it>::iterator_category> , "Can't remove items this way unless Items_it is a random access iterator"); size_t indices_size = std::distance(indices_begin, indices_end); size_t items_size = std::distance(items_begin, items_end); if (indices_size == 0) { // Nothing to erase return items_end; } #if !defined(NDEBUG) || defined(_DEBUG) // Debug check to see if the indices are already sorted and are less than // size of items. assert(indices_begin[0] < items_size); for (int i = 1; i < indices_size; ++i) { assert(indices_begin[i - 1] < indices_begin[i]); assertstd::is_sorted(indices_begin[i] <indices_begin, items_sizeindices_end)); } #endif auto last = items_begin; auto shift = [&last, &items_begin](size_t range_begin, size_t range_end) { std::copy(items_begin + range_begin, items_begin + range_end, last); last += range_end - range_begin; }; size_t last_index = -1; for (size_t i = 0; i != indices_size; ++i) { shift(last_index + 1, indices_begin[i]); last_index = indices_begin[i]; } shift(last_index + 1, items_size); return last; } template <typename Items_it, typename Indices_it> auto remove_indices( Items_it items_begin, Items_it items_end , Indices_it indices_begin, Indices_it indices_end ) { static_assert( std::is_same_v<std::random_access_iterator_tag , typename std::iterator_traits<Items_it>::iterator_category> , "Can't remove items this way unless Items_it is a random access iterator"); size_t indices_size = std::distance(indices_begin, indices_end); size_t items_size = std::distance(items_begin, items_end); if (indices_size == 0) { // Nothing to erase return items_end; } #if !defined(NDEBUG) || defined(_DEBUG) // Debug check to see if the indices are already sorted and are less than // size of items. assert(indices_begin[0] < items_size); for (int i = 1; i < indices_size; ++i) { assert(indices_begin[i - 1] < indices_begin[i]); assert(indices_begin[i] < items_size); } #endif auto last = items_begin; auto shift = [&last, &items_begin](size_t range_begin, size_t range_end) { std::copy(items_begin + range_begin, items_begin + range_end, last); last += range_end - range_begin; }; size_t last_index = -1; for (size_t i = 0; i != indices_size; ++i) { shift(last_index + 1, indices_begin[i]); last_index = indices_begin[i]; } shift(last_index + 1, items_size); return last; } template <typename Items_it, typename Indices_it> auto remove_indices( Items_it items_begin, Items_it items_end , Indices_it indices_begin, Indices_it indices_end ) { static_assert( std::is_same_v<std::random_access_iterator_tag , typename std::iterator_traits<Items_it>::iterator_category> , "Can't remove items this way unless Items_it is a random access iterator"); size_t indices_size = std::distance(indices_begin, indices_end); size_t items_size = std::distance(items_begin, items_end); if (indices_size == 0) { // Nothing to erase return items_end; } // Debug check to see if the indices are already sorted and are less than // size of items. assert(indices_begin[0] < items_size); assert(std::is_sorted(indices_begin, indices_end)); auto last = items_begin; auto shift = [&last, &items_begin](size_t range_begin, size_t range_end) { std::copy(items_begin + range_begin, items_begin + range_end, last); last += range_end - range_begin; }; size_t last_index = -1; for (size_t i = 0; i != indices_size; ++i) { shift(last_index + 1, indices_begin[i]); last_index = indices_begin[i]; } shift(last_index + 1, items_size); return last; } This is an algorithm based on Andriy Tylychko's answer so that this can make it easier and faster to use the answer, without having to pick it apart. It also removes the need to have -1 at the beginning of the indices list and a number of items at the end. Also some debugging code to make sure the indices are valid (sorted and valid index into items).
template <typename Items_it, typename Indices_it> auto remove_indices( Items_it items_begin, Items_it items_end , Indices_it indices_begin, Indices_it indices_end ) { static_assert( std::is_same_v<std::random_access_iterator_tag , typename std::iterator_traits<Items_it>::iterator_category> , "Can't remove items this way unless Items_it is a random access iterator"); size_t indices_size = std::distance(indices_begin, indices_end); size_t items_size = std::distance(items_begin, items_end); if (indices_size == 0) { // Nothing to erase return items_end; } #if !defined(NDEBUG) || defined(_DEBUG) // Debug check to see if the indices are already sorted and are less than // size of items. assert(indices_begin[0] < items_size); for (int i = 1; i < indices_size; ++i) { assert(indices_begin[i - 1] < indices_begin[i]); assert(indices_begin[i] < items_size); } #endif auto last = items_begin; auto shift = [&last, &items_begin](size_t range_begin, size_t range_end) { std::copy(items_begin + range_begin, items_begin + range_end, last); last += range_end - range_begin; }; size_t last_index = -1; for (size_t i = 0; i != indices_size; ++i) { shift(last_index + 1, indices_begin[i]); last_index = indices_begin[i]; } shift(last_index + 1, items_size); return last; } Here is an example of usage:
template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T>& v) { for (auto i : v) { os << i << " "; } os << std::endl; return os; } int main() { using std::begin; using std::end; std::vector<int> items = { 1, 3, 6, 8, 13, 17 }; std::vector<int> indices = { 0, 1, 2, 3, 4 }; std::cout << items; items.erase( remove_indices(begin(items), end(items), begin(indices), end(indices)) , std::end(items) ); std::cout << items; return 0; } Output:
1 3 6 8 13 17 17 The headers required are:
#include <iterator> #include <vector> #include <iostream> // only needed for output #include <cassert> #include <type_traits> And a Demo can be found on godbolt.org.
lang-cpp