본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : std::remove, std::sort, std::replace (98, 20)

by snowoods 2025. 9. 11.

Modern C++

std::remove, std::sort, std::replace

 

개요

C++ 표준 라이브러리에서 제공하는 std::remove, std::sort, std::replace 계열 함수들은 컨테이너의 원소를 정렬하거나, 특정 조건에 맞는 원소를 바꾸거나, 제거하는 데 사용되는 강력한 도구입니다. 이 함수들을 사용하면 복잡한 반복문 없이도 간결하고 효율적인 코드를 작성할 수 있습니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C++98: std::sort, std::replace, std::replace_if, std::remove, std::remove_if
  • C++20: std::erase, std::erase_if (컨테이너용 비멤버 함수)

 

내용 설명

  • std::sort: 지정된 범위의 원소들을 오름차순 또는 사용자가 정의한 비교 함수에 따라 정렬합니다.
  • std::replace, std::replace_if: 범위 내에서 특정 값이나 조건을 만족하는 원소들을 새로운 값으로 교체합니다.
  • std::remove, std::remove_if: 범위 내에서 특정 값이나 조건을 만족하는 원소들을 뒤로 보내고, 제거되지 않은 원소들의 새로운 끝을 가리키는 반복자를 반환합니다. 이 함수들은 컨테이너의 크기를 직접 변경하지 않으므로, 보통 컨테이너의 erase 멤버 함수와 함께 사용됩니다 (Erase-Remove Idiom).
  • std::erase, std::erase_if (C++20): C++20부터 도입된 비멤버 함수로, 컨테이너에서 특정 값이나 조건을 만족하는 원소를 직접 제거하여 Erase-Remove Idiom을 더 간결하게 사용할 수 있게 해줍니다.

 

예제 코드

#include <algorithm>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <random>
#include <vector>

namespace
{
constexpr auto NUM_ELEMENTS = size_t{10U};
}; // namespace

std::int32_t gen()
{
    static auto seed = std::random_device{};
    static auto g = std::mt19937{seed()};
    static auto d = std::uniform_int_distribution<std::int32_t>{-10, 10};

    return d(g);
}

template <typename T>
void print_vector(const std::vector<T> &vec)
{
    for (const auto v : vec)
    {
        std::cout << v << ' ';
    }
    std::cout << '\n';
}

int main()
{
    auto my_vector = std::vector<std::int32_t>(NUM_ELEMENTS, 0U);
    std::generate(my_vector.begin(), my_vector.end(), gen);
    std::cout << "Initial: ";
    print_vector(my_vector);

    std::replace(my_vector.begin(), my_vector.end(), 0, 1000);
    std::cout << "After replace 0 with 1000: ";
    print_vector(my_vector);

    std::replace_if(
        my_vector.begin(),
        my_vector.end(),
        [](const auto val) { return val % 2 == 0; },
        -1);
    std::cout << "After replace even numbers with -1: ";
    print_vector(my_vector);

    std::sort(my_vector.begin(),
              my_vector.end(),
              [](const auto i, const auto j) { return i < j; });
    std::cout << "Sorted (ascending): ";
    print_vector(my_vector);

    std::sort(my_vector.begin(),
              my_vector.end(),
              [](const auto i, const auto j) { return i > j; });
    std::cout << "Sorted (descending): ";
    print_vector(my_vector);

    // Pre C++20 (Erase-Remove Idiom)
    auto remove_it = std::remove(my_vector.begin(), my_vector.end(), -1);
    my_vector.erase(remove_it, my_vector.end());
    std::cout << "After remove -1 (C++98): ";
    print_vector(my_vector);

    // Post C++20
    std::erase(my_vector, 7);
    std::cout << "After erase 7 (C++20): ";
    print_vector(my_vector);

    std::erase_if(my_vector, [](const auto i) { return i < 0; });
    std::cout << "After erase_if negative (C++20): ";
    print_vector(my_vector);

    return 0;
}

 

실행 결과

Initial: 8 -5 5 -1 5 10 -2 5 -10 3 
After replace 0 with 1000: 8 -5 5 -1 5 10 -2 5 -10 3 
After replace even numbers with -1: -1 -5 5 -1 5 -1 -1 5 -1 3 
Sorted (ascending): -5 -1 -1 -1 -1 3 5 5 5 
Sorted (descending): 5 5 5 3 -1 -1 -1 -1 -5 
After remove -1 (C++98): 5 5 5 3 -5 
After erase 7 (C++20): 5 5 5 3 -5 
After erase_if negative (C++20): 5 5 5 3 

실행 결과는 난수 생성에 따라 달라질 수 있습니다.

활용팁

  • Erase-Remove Idiom 이해: std::remove는 원소를 실제로 삭제하지 않고 뒤로 미룰 뿐이라는 점을 기억해야 합니다. C++20 이전 버전에서는 container.erase(std::remove(begin, end, value), end) 형태의 코드가 필수적이었습니다.
  • C++20 활용: C++20 이상을 사용한다면 std::erasestd::erase_if를 사용하여 코드를 더 간결하고 안전하게 작성할 수 있습니다.
  • 성능: std::sort는 평균적으로 O(N log N)의 복잡도를 가집니다. std::remove, std::replace 계열 함수들은 O(N)의 복잡도를 가지므로, 대량의 데이터 처리 시에도 효율적입니다.