Ștergerea elementelor și idiomul Remove-Erase Idiom
Idiomul remove-erase idiom este destinat pentru a rezolva problema ștergerii elementelor dintr-un container, deoarece această problemă poate deveni destul de complicată și poate duce la erori. Acest idiom presupune utilizarea algoritmului remove() sau remove_if(), urmat de apelul funcției erase() a containerului.
Când sunt aplicate algoritmii remove() și remove_if(), elementele care trebuie păstrate sunt mutate la începutul containerului, iar funcțiile remove() și remove_if() returnează un iterator care indică primul element ce trebuie șters. Apoi, acest iterator este trecut în funcția erase(), care efectuează efectiv ștergerea elementelor. Implementarea idiomului este următoarea:
#include <iostream>
#include <vector>
#include <algorithm>
void print(const std::vector<int>& data)
{
for (const auto& n : data)
{
std::cout << n << "\t";
}
std::cout << std::endl;
}
bool is_negative(int n) { return n < 0; }
int main()
{
std::vector<int> numbers { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 };
// aplicăm algoritmul remove_if() pentru a elimina toate elementele care nu corespund condiției
auto iter{ std::remove_if(begin(numbers), end(numbers), is_negative) };
print(numbers); // 0 1 2 3 4 5 1 2 3 4 5
// ștergem toate elementele începând de la iteratorul first_to_erase
numbers.erase(iter, end(numbers));
print(numbers); // 0 1 2 3 4 5
}
În acest exemplu, eliminăm toate numerele negative din vectorul numbers. Mai întâi, apelăm funcția std::remove_if():
auto iter{ std::remove_if(begin(numbers), end(numbers), is_negative) };
Aceasta primește iteratorii pentru începutul și sfârșitul intervalului de unde trebuie să ștergem numerele. În acest caz, intervalul este definit de iteratorii pentru începutul și sfârșitul vectorului. Al treilea parametru reprezintă condiția – o funcție care primește un anumit număr și returnează un rezultat de tip bool (true dacă numărul corespunde condiției și false dacă nu). În acest caz, condiția este funcția is_negative, care verifică dacă numărul este negativ.
După executarea funcției remove_if, elementele care trebuie păstrate (adică numerele pozitive și 0) sunt mutate la începutul vectorului. Partea de la sfârșit a vectorului conține elementele care urmează să fie șterse, dar acest lucru nu contează, deoarece vom șterge această secțiune.
Apoi, folosim erase() pentru a șterge efectiv acele elemente:
numbers.erase(iter, end(numbers));
Astfel, vectorul numbers va conține doar numerele care nu sunt negative.
std::erase_if()
Deoarece ștergerea sigură a elementelor din containere reprezintă o sarcină frecvent întâlnită, începând cu standardul C++20, au fost adăugate funcțiile std::erase() și std::erase_if().
Funcția std::erase() elimină o valoare specifică dintr-un container (nu se aplică pentru std::set și std::map):
std::erase(Container, Value)
Funcția std::erase_if() elimină valorile dintr-un container care corespund unei condiții:
std::erase_if(Container, Function)
Așadar, să rescriem exemplul anterior utilizând funcția std::erase_if:
#include <iostream>
#include <vector>
void print(const std::vector<int>& data)
{
for (const auto& n : data)
{
std::cout << n << "\t";
}
std::cout << std::endl;
}
bool is_negative(int n) { return n < 0; }
int main()
{
std::vector<int> numbers { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 };
std::erase_if(numbers, is_negative);
print(numbers); // 0 1 2 3 4 5
}
Acum, folosind std::erase_if(), toate valorile care corespund condiției sunt eliminate din vectorul numbers într-un singur pas, fără a fi nevoie de remove_if și erase separate.