HOWTO · C++

Rimuovi elemento dal vettore in C++

Questo articolo mostra come rimuovere un elemento dal vettore in C++

In questa pagina

Per rimuovere una posizione o un intervallo specifico, usa std::vector::erase. Per rimuovere in C++20 tutti gli elementi uguali a un valore, usa std::erase; con gli standard C++ precedenti, combina std::remove e erase secondo l’idioma erase-remove. Gli esempi mostrano come ciascuna scelta modifica il vettore e come mantenere validi gli iteratori.

Rimuovere un elemento con std::vector::erase

std::vector::erase accetta un iteratore a un singolo elemento oppure un intervallo semiaperto [first, last). L’intervallo include first ed esclude last. L’overload per un singolo elemento richiede un iteratore valido che punti a un elemento. Restituisce un iteratore all’elemento successivo all’intervallo cancellato oppure end() se non ci sono elementi successivi.

Questo esempio completo rimuove l’elemento all’indice 2 e poi legge l’iteratore restituito:

Esegui questo esempio:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers{10, 20, 30, 40, 50};
    auto next = numbers.erase(numbers.begin() + 2);

    for (std::size_t i = 0; i < numbers.size(); ++i) {
        if (i != 0) std::cout << ' ';
        std::cout << numbers[i];
    }
    std::cout << "\nNext value: " << *next << '\n';
}
10 20 40 50
Next value: 40

La cancellazione invalida gli iteratori e i riferimenti che si trovano in corrispondenza della posizione cancellata o dopo di essa. Aggiorna o scarta questi riferimenti prima di riutilizzarli. Per cancellare un intervallo, passa l’iteratore al suo primo elemento e quello che punta all’elemento successivo all’ultimo; l’iteratore restituito e le regole di invalidazione sono gli stessi. Se l’intervallo cancellato arriva alla fine, l’iteratore restituito coincide con numbers.end() e non può essere dereferenziato.

Rimuovere tutti i valori corrispondenti con std::erase (C++20)

C++20 aggiunge l’overload non membro std::erase per std::vector, dichiarato tramite <vector>. Rimuove tutti gli elementi uguali al valore fornito e restituisce il numero di elementi rimossi. Rimuove anche le occorrenze ripetute, quindi è utile quando la selezione si basa sull’uguaglianza. Gli standard precedenti non forniscono questo overload; usa l’idioma erase-remove descritto nella sezione successiva.

Questo esempio usa std::erase quando viene compilato come C++20 e ricorre all’idioma erase-remove con uno standard precedente. Entrambi i rami rimuovono tutte le occorrenze di 2 e stampano il numero di elementi rimossi:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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

int main() {
    std::vector<int> numbers{1, 2, 3, 2, 4, 2, 5};
    std::size_t removed;

#if __cplusplus >= 202002L
    removed = std::erase(numbers, 2);
#else
    const auto old_size = numbers.size();
    const auto new_end = std::remove(numbers.begin(), numbers.end(), 2);
    numbers.erase(new_end, numbers.end());
    removed = old_size - numbers.size();
#endif

    std::cout << "Removed: " << removed << "\nRemaining: ";
    for (std::size_t i = 0; i < numbers.size(); ++i) {
        if (i != 0) std::cout << ' ';
        std::cout << numbers[i];
    }
    std::cout << '\n';
}
Removed: 3
Remaining: 1 3 4 5

L’overload di std::erase per std::vector è disponibile a partire da C++20 e restituisce il numero di elementi rimossi. Il ramo per gli standard precedenti usa l’idioma erase-remove, spiegato qui sotto.

Rimuovere i valori corrispondenti con l’idioma erase-remove

Prima di C++20, combina std::remove di <algorithm> con il metodo membro erase del vettore. std::remove sposta i valori non corrispondenti verso l’inizio dell’intervallo scrivibile e restituisce un iteratore alla nuova fine logica. Non modifica la dimensione del vettore: finché non viene cancellato l’intervallo finale, il vettore contiene ancora lo stesso numero di elementi. Passa a erase l’iteratore restituito e l’end() originale del vettore per ridurre la dimensione del contenitore.

Questo esempio rimuove tutte le occorrenze di 2 con questa procedura in due passaggi:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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

int main() {
    std::vector<int> numbers{1, 2, 3, 2, 4, 5};
    const auto new_end = std::remove(numbers.begin(), numbers.end(), 2);
    numbers.erase(new_end, numbers.end());

    for (std::size_t i = 0; i < numbers.size(); ++i) {
        if (i != 0) std::cout << ' ';
        std::cout << numbers[i];
    }
    std::cout << '\n';
}

L’intervallo passato a std::remove deve essere scrivibile, perché l’algoritmo riordina i valori conservati. L’accesso in sola lettura a un contenitore tramite iteratori const non impedisce di usare questo idioma, purché sia disponibile un intervallo di iteratori scrivibile.

Cancellare elementi in sicurezza durante l’iterazione

Quando è una condizione o un predicato a determinare quali elementi rimuovere, usa un ciclo con iteratore. Dopo la cancellazione, assegna all’iteratore quello restituito da erase; incrementalo solo se l’elemento corrente rimane. L’iteratore cancellato non è più valido: incrementarlo o dereferenziarlo è quindi rischioso.

Questo esempio rimuove i valori pari mantenendo valido l’iteratore:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers{1, 2, 3, 4, 5, 6, 7};
    for (auto it = numbers.begin(); it != numbers.end();) {
        if (*it % 2 == 0) {
            it = numbers.erase(it);
        } else {
            ++it;
        }
    }

    for (std::size_t i = 0; i < numbers.size(); ++i) {
        if (i != 0) std::cout << ' ';
        std::cout << numbers[i];
    }
    std::cout << '\n';
}

In C++20 usa std::erase per rimuovere in base all’uguaglianza; con gli standard precedenti, usa l’idioma erase-remove per lo stesso scopo. Se invece la rimozione dipende da una condizione, usa erase in un ciclo e aggiorna l’iteratore con quello restituito. La scelta dipende dal fatto che la selezione riguardi una posizione o un intervallo specifico, l’uguaglianza con un valore oppure un predicato valutato durante l’iterazione.