Copierea elementelor
Funcția std::copy_if() se folosește pentru a copia valorile care îndeplinesc un anumit criteriu dintr-un interval într-altul. Această funcție are mai multe versiuni, iar una dintre ele este următoarea:
iterator std::copy_if(start_source_iterator, end_source_iterator, start_dest_iterator, condition)
Primele două argumente – start_source_iterator și end_source_iterator – reprezintă iteratorii de început și sfârșit ai intervalului de valori din care trebuie să copiem valorile. Al treilea argument – start_dest_iterator – reprezintă iteratorul de început al intervalului în care trebuie să inserăm valorile copiate.
Ultimul parametru – condition – reprezintă condiția. Condiția este o funcție care primește o valoare de tip arbitrar și returnează un rezultat de tip bool - true dacă valoarea satisface condiția și false dacă nu o satisface.
Rezultatul funcției este un iterator care indică adresa imediat următoare ultimei valori copiate din intervalul sursă.
Să vedem un exemplu de utilizare a funcției:
#include <iostream>
#include <vector>
#include <algorithm>
// dacă numărul este par
bool is_even(int n) { return n % 2 == 0; }
// dacă numărul este pozitiv
bool is_positive(int n) { return n > 0; }
// pentru a afișa un vector pe consolă
void print(const std::vector<int>& data)
{
for (const auto& n : data)
{
std::cout << n << "\t";
}
std::cout << std::endl;
}
int main()
{
std::vector<int> numbers { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 };
std::vector<int> even_numbers(numbers.size());
std::vector<int> pos_numbers(numbers.size());
auto end_even_iter = std::copy_if(begin(numbers), end(numbers), begin(even_numbers), is_even);
even_numbers.erase(end_even_iter, end(even_numbers));
print(even_numbers); // -4 -2 0 2 4
auto end_pos_iter = std::copy_if(begin(numbers), end(numbers), begin(pos_numbers), is_positive);
pos_numbers.erase(end_pos_iter, end(pos_numbers));
print(pos_numbers); // 1 2 3 4 5
}
În acest exemplu, copiem din vectorul de numere numbers în două alte vectori – even_numbers și pos_numbers. Observați că pentru ambii vectori finali este setată lungimea:
std::vector<int> even_numbers(numbers.size());
std::vector<int> pos_numbers(numbers.size());
Teoretic, toate elementele din vectorul numbers ar putea îndeplini condiția, de aceea lungimea vectorilor even_numbers și pos_numbers este setată la dimensiunea vectorului numbers.
Condițiile sunt definite prin funcțiile separate – is_even (verifică dacă numărul este par) și is_positive (verifică dacă numărul este pozitiv).
Mai întâi, copiem din vectorul numbers în vectorul even_numbers toate numerele care sunt pare:
auto end_even_iter = std::copy_if(begin(numbers), end(numbers), begin(even_numbers), is_even);
Aici, intervalul de căutare este definit de iteratorii pentru începutul și sfârșitul vectorului numbers. Elementele copiate vor fi plasate de la începutul vectorului even_numbers, iar condiția va fi funcția is_even.
De fapt, funcționarea acestei funcții ar putea arăta astfel:
auto output_iter = begin(even_numbers);
for (auto input_iter = begin(numbers); input_iter != end(numbers); ++input_iter)
{
if (*input_iter % 2 == 0)
{
*output_iter++ = *input_iter; // atribuim valorile prin iteratorul de destinație
}
}
// returnăm iteratorul
auto end_even_iter = output_iter;
Astfel, toate numerele pare din numbers vor fi copiate în even_numbers. Dar există o problemă – nu toate elementele din vectorul numbers vor fi pare, iar dimensiunea inițială a vectorului even_numbers este aceeași cu a vectorului numbers. Pentru a păstra doar numărul de elemente copiate în even_numbers, tăiem vectorul even_numbers până la poziția ultimului element copiat, la care se referă iteratorul end_even_iter.
even_numbers.erase(end_even_iter, end(even_numbers));
În mod similar, copiem numerele pozitive, dar folosim funcția is_positive ca condiție:
auto end_pos_iter = std::copy_if(begin(numbers), end(numbers), begin(pos_numbers), is_positive);
pos_numbers.erase(end_pos_iter, end(pos_numbers));
Ieșirea pe consolă va fi:
-4 -2 0 2 4
1 2 3 4 5