2016-03-02 15 views
6

W mojej głównej funkcji zadeklarowałem std::vector<int> i chcę usunąć z niego wszystkie elementy parzyste, ale tylko przekazując iterator do funkcji o nazwie remove_even, która akceptuje iterator początkowy i końcowy kontenera.Usuwanie elementu kontenera przez bezpośredni dostęp do jego iteratora samemu w C++

#include <iostream> 
#include <algorithm> 
#include <vector> 

void remove_even(auto start, auto end) { 
    while(start != end) { 
     if(*start % 2 == 0) 
     // Remove element from container 
    } 
} 

int main() { 
    std::vector<int> vec = {2, 4, 5, 6, 7}; 
    remove_even(vec.begin(), vec.end()); 
} 

Czy jest sposób na to zrobić w C++ lub muszę bezpośrednio przekazać w moim wektorze do funkcji?

+4

nr usunięcie jest działanie na 'sam wektor'. Wzorzec standardowego algorytmu ['std :: remove_if'] (http://en.cppreference.com/w/cpp/algorithm/remove) polega na przesunięciu elementów na koniec, a następnie ich usunięciu. ] (https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom) – BoBTFish

+0

Nie można zadać sobie trudu, aby napisać pełną odpowiedź, ale jedną linijką do zrobienia tego jest: 'vec.erase (std :: remove_if (std :: begin (vec), std :: end (vec), [] (int i) {return! (i% 2);}), std :: end (vec)); ' – BoBTFish

Odpowiedz

5

Jest to klasa sama w sobie, że ma std::vector metodę erase który pozwala wymazać wymaganych elementów w wektorze.

Wszystko, co możesz zrobić za pomocą iteratorów, to wywołanie standardowego algorytmu std::remove_if, który następnie użyje zwróconego iteratora w wywołaniu metody erase.

Na przykład

#include <iostream> 
#include <vector> 
#include <algorithm> 

std::vector<int>::iterator remove_even(std::vector<int>::iterator first, 
             std::vector<int>::iterator last) 
{ 
    return std::remove_if(first, last, [](int x) { return x % 2 == 0; }); 
} 

int main() 
{ 
    std::vector<int> vec = { 2, 4, 5, 6, 7 }; 

    for (int x : vec) std::cout << x << ' '; 
    std::cout << std::endl; 

    vec.erase(remove_even(vec.begin(), vec.end()), vec.end()); 

    for (int x : vec) std::cout << x << ' '; 
    std::cout << std::endl; 
}  

Wyjście programu jest

2 4 5 6 7 
5 7 
1

Jest to poprawny sposób IMO:

template <typename T> 
T remove_even(T start, T end) { 
    return std::remove_if(start,end,[](const auto& item){return item%2==0;}; 
} 

int main() { 
    std::vector<int> vec = {2, 4, 5, 6, 7}; 
    vec.erase(remove_even(vec.begin(), vec.end()),vec.end()); 
} 
+1

Czy możesz wyjaśnić, dlaczego podejście, którego chciał, nie będzie działać, i co jest dobre w twoim podejściu? – BoBTFish

+0

Próbuję naśladować sposób działania standardowej biblioteki. Imitowałem zachowanie remove_if –

+0

błędu: "auto" nie jest dozwolone w prototypie funkcji –

0

Jest żaden sposób to zrobić przekazując tylko te iteratory do funkcji. Trzeba przejść cały wektor do funkcji, jak pokazano poniżej:

void remove_even(std::vector<int> v) 
{ 
    for (auto it = v.begin(); it != v.end();) { 
     if ((*it) % 2 == 0) { 
      it = v.erase(it); 
     } else { 
      ++it; 
     } 
    } 
} 
1

Od cplusplus.com:

An iterator is any object that, pointing to some element in a range of elements (such as an array or a container), has the ability to iterate through the elements of that range using a set of operators (with at least the increment (++) and dereference (*) operators).

Jak stwierdzono w środki, wskazuje na element w szeregu elementów. Nie trzeba podawać żadnych informacji o zakresie, w jakim się znajduje, tj. O pojemniku, w którym są przechowywane elementy.

Jak już stwierdzono w komentarzu, usunięcie elementu z wektora (lub dowolnego innego pojemnika) jest operacja, która wpływa na pojemnik, a nie tylko obiekt. Dlatego zawsze będziesz musiał zadzwonić pod numer erase() lub podobną funkcję na pojemniku.

co prosicie o to (nieco, nie dokładnie) podobny do tego:

void remove_or_not(int& i){ 
    //do something with i to remove it from a container 
    //but we dont have a container here 
} 

int main(){ 
    std::vector<int> vec; 
    //fill vec and generate some int n 
    remove_or_not(vec[n]); 
} 

Dzwoniąc remove_or_not() w powyższym przykładzie, po prostu przekazać odwołanie do int - możemy całkowicie stracił informacji jest w pojemniku, więc jest oczywiste, że nie możemy go usunąć z żadnego pojemnika.

Próbując tego samego z iteratorami nadal mamy informację, że element znajduje się w pojemniku - ale mogliśmy utracić informacje w tym pojemniku, ponieważ nie jest to wymagane przez iterator do przechowywania tych informacji.
E.g. Iterator nad tablicą w stylu C może być po prostu wskaźnikiem. Możemy go zwiększać i zmniejszać i porównywać z wskaźnikiem do pierwszego elementu i wskaźnikiem za ostatnim elementem. Ale nie ma potrzeby, aby cokolwiek wiedzieć o wielkości tablicy lub tablicy.

PS: w przypadku podejść do tego, jak poprawnie wdrożyć to, czego potrzebujesz, zobacz już opublikowane odpowiedzi, nie sądzę, że istnieje potrzeba ich powtórzenia.

0

Możesz rozważyć idiom przekazywania kontenera przez odwołanie do wartości x, a następnie zwrócenie kopii/przeniesionej wersji.

Daje to korzyści wydajności w połączeniu z czytelnością na stronie wywołania.

Rozszerzony przykład poniżej.

Należy zauważyć, że wszystkie "pozorne" kopiowanie wektora zostanie usunięte (zoptymalizowane) przez kompilator (RVO).

Należy również pamiętać, że ponieważ Container&& jest oceniany w kontekście wywnioskowania, automatycznie będzie to albo const Container& lub Container&&, w zależności od tego, które z nich jest najbardziej odpowiednie w kontekście strony wywoławczej. Oznacza to, że sama funkcja może być wykorzystane zarówno do mutacji istniejącego kontenera (x = sorted(x)) lub do zmutowanego kopii (x = sorted(y))

#include <iostream> 
#include <vector> 
#include <algorithm> 

template<class Container, class Predicate> 
auto all_except(Container&& c, Predicate&& pred) 
{ 
    auto result = std::forward<Container>(c); 

    result.erase(std::remove_if(std::begin(result), 
           std::end(result), 
           std::forward<Predicate>(pred)), 
       std::end(result)); 
    return result; 
} 

template<class Container> 
auto sorted(Container&& c) 
{ 
    auto result = std::forward<Container>(c); 
    std::sort(std::begin(result), std::end(result)); 
    return result; 
} 

auto even_items = [](auto const& item) { return item % 2 == 0; }; 
auto odd_items = [](auto const& item) { return item % 2 != 0; }; 


template<class Vec> 
void emit(const Vec& v) 
{ 
    std::cout << "["; 
    auto sep = " "; 
    for (auto const& e : v) { 
     std::cout << sep << e; 
     sep = ", "; 
    } 
    std::cout << " ]" << std::endl; 
} 

int main() 
{ 
    std::vector<int> vec = {65, 2, 32, 63, 9, 13, 88, 22, 4, 5, 6, 7}; 

    emit(all_except(vec, even_items)); 
    emit(all_except(vec, odd_items)); 

    emit(all_except(sorted(vec), odd_items)); 

    vec = sorted(all_except(std::move(vec), even_items)); 
    emit(vec); 

    return 0; 
} 

oczekiwane wyniki:

[ 65, 63, 9, 13, 5, 7 ] 
[ 2, 32, 88, 22, 4, 6 ] 
[ 2, 4, 6, 22, 32, 88 ] 
[ 5, 7, 9, 13, 63, 65 ] 
Powiązane problemy