Можно ли расширить идиому "стереть-удалить" для одновременной работы с несколькими контейнерами?
В C++ идиома erase-remove - отличный способ удалить все элементы стандартного контейнера, которые удовлетворяют заданному критерию.
Можно ли расширить идиому удаления-удаления, чтобы работать с несколькими контейнерами одновременно?
То есть можно ли вызвать что-то похожее на erase-remove
на одном контейнере, а также удалить соответствующие элементы в другом контейнере?
В моем конкретном случае все контейнеры std::vectors
того же размера.
Например, если элементы 0
, 3
, а также 5
удаляются из первого контейнера, я хотел бы элементы 0
, 3
, а также 5
также удаляется из второго контейнера.
Можно, например, предварительно вычислить контейнер, помечающий удаляемые элементы, построить предикат для remove_if
который просто индексирует в контейнер флага и вызывает erase-remove
многократно.
Можно ли делать то, что я хочу, без предварительного вычисления?
1 ответ
Да, ты можешь это сделать. Работает как есть. Я предоставляю пример кода и объясняю его.
// initialises a vector that holds the numbers from 0-9.
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
print(v);
// removes all elements with the value 5
v.erase( std::remove( v.begin(), v.end(), 5 ), v.end() );
print(v);
В этом коде мы хотим удалить 5
из вектора v
и мы используем std::remove
а также std::erase
после этого. Вы должны понять, что делает std::remove
делать. std::remove
Меняет местами элементы в контейнере так, что все удаляемые элементы идут до конца, и эта функция возвращает итератор к первому удаляемому элементу. следующий std::erase
берет этот итератор и удаляет все элементы, начиная с этого итератора и до конца контейнера (фактически до второго аргумента. v.end()
в нашем случае).
std::vector<int> v1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::vector<int> v2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
auto removeIt=std::remove( v1.begin(), v1.end(), [](const int value){
return value==0 || value==3 || value==5;
} );
// now v1 = { 1, 2, 4, 6, 7, 8, 9, 0, 3, 5 } and removeIt points to 0 element (next after 9)..
// removeIt = ^
std::erase(std::remove_if(v2.begin(),v2.end(),[&](const int value){
// remove every object that can be found between removeIt and v1.end()
return std::find(removeIt,v1.end(),value)!=v1.end();
}), v2.end());
// now v2 = { 1, 2, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }
std::erase(removeIt,v1.end());
// now v1 = { 1, 2, 4, 6, 7, 8, 9 }
Этот код делает именно то, что вам нужно.