HOWTO · C++

C++의 벡터에서 요소 제거

이 기사에서는 C++의 벡터에서 요소를 제거하는 방법을 보여줍니다.

이 페이지의 내용

알려진 위치나 범위의 요소를 제거하려면 std::vector::erase를 호출합니다. C++20에서는 특정 값과 같은 모든 요소를 제거할 때 std::erase를 사용하고, 이전 C++ 표준에서는 std::remove와 erase를 결합하는 erase-remove 관용구를 사용합니다. 예제에서는 각 방법이 벡터를 어떻게 바꾸는지, 반복자를 유효하게 유지하는 방법은 무엇인지 살펴봅니다.

std::vector::erase로 요소 하나 제거

std::vector::erase에는 요소 하나를 가리키는 반복자나 반개구간 [first, last)을 전달할 수 있습니다. 이 범위에는 first가 포함되고 last는 포함되지 않습니다. 단일 요소를 지우는 오버로드에는 요소를 가리키는 유효한 반복자가 필요합니다. 지운 범위 다음에 요소가 있으면 그 요소를 가리키는 반복자를 반환하고, 다음 요소가 없으면 end()를 반환합니다.

다음 전체 예제는 인덱스 2의 요소를 지운 뒤 반환된 반복자를 읽습니다.

이 예제를 실행해 보세요.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#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

요소를 지우면 지운 위치와 그 뒤에 있는 반복자와 참조가 무효화됩니다. 다시 사용하기 전에 해당 핸들을 갱신하거나 버리세요. 범위를 지우려면 범위의 첫 반복자와 마지막 요소 바로 다음을 가리키는 반복자를 전달합니다. 반환되는 반복자와 무효화 규칙은 같습니다. 지운 범위가 끝까지 이어지면 반환된 반복자는 numbers.end()와 같으며 역참조할 수 없습니다.

std::erase(C++20)로 일치하는 값 모두 제거

C++20부터 <vector>를 통해 선언되는 비멤버 std::erase 오버로드를 std::vector에 사용할 수 있습니다. 이 함수는 지정한 값과 같은 요소를 모두 제거하고 제거한 요소의 수를 반환합니다. 같은 값이 여러 번 나타나도 모두 제거되므로 값의 일치 여부를 기준으로 선택할 때 유용합니다. 이전 언어 표준에는 이 오버로드가 없으므로 다음 섹션의 erase-remove 관용구를 사용하세요.

이 예제는 C++20으로 컴파일할 때 std::erase를 사용하고, 이전 표준에서는 erase-remove 관용구를 사용합니다. 두 분기 모두 2가 나타나는 항목을 전부 제거하고 제거한 개수를 출력합니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#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

std::vector용 std::erase 오버로드는 C++20부터 사용할 수 있으며 제거한 개수를 반환합니다. 이전 표준을 위한 분기에서는 아래에서 설명하는 erase-remove 관용구를 사용합니다.

erase-remove 관용구로 일치하는 값 제거

C++20 이전에는 <algorithm>의 std::remove와 벡터 멤버 함수 erase를 함께 사용합니다. std::remove는 일치하지 않는 값을 쓰기 가능한 범위의 앞쪽으로 옮기고, 값들이 옮겨진 뒤의 논리적 끝을 가리키는 반복자를 반환합니다. 이 함수만으로는 벡터의 크기가 바뀌지 않습니다. 뒤쪽 범위를 지우기 전까지 벡터에는 원래 개수만큼의 요소가 남아 있습니다. 반환된 반복자와 벡터의 기존 end()를 erase에 전달하면 컨테이너 크기가 줄어듭니다.

다음 예제는 이 두 단계 방식으로 2가 나타나는 항목을 모두 제거합니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#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';
}

std::remove에 전달하는 범위는 쓰기 가능해야 합니다. 알고리즘이 남길 값을 재배치하기 때문입니다. 컨테이너에서 const 반복자를 통한 읽기 전용 접근을 제공하더라도, 쓰기 가능한 반복자 범위가 있으면 이 관용구를 사용할 수 있습니다.

반복 중 요소를 안전하게 지우기

조건자나 다른 조건에 따라 제거할 요소를 결정할 때는 반복자 루프를 사용하세요. 요소를 지운 뒤에는 erase가 반환한 반복자를 대입하고, 현재 요소를 유지할 때만 반복자를 증가시킵니다. 지워진 반복자는 무효이므로 이전 반복자를 증가시키거나 역참조하면 안전하지 않습니다.

다음 예제는 반복자를 유효하게 유지하면서 짝수를 제거합니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#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';
}

C++20에서 값의 일치 여부로 제거할 때는 std::erase를, 이전 표준에서 같은 작업을 할 때는 erase-remove 관용구를 사용하세요. 조건에 따라 제거할 때는 반환된 반복자를 사용하는 루프와 erase를 함께 사용합니다. 어떤 방법을 선택할지는 제거 기준이 알려진 위치나 범위인지, 값과의 일치 여부인지, 아니면 루프를 진행하며 평가하는 조건자인지에 따라 달라집니다.