[C++23 새기능 소개] std::ranges::drop_last, std::ranges::drop_last_while

C++23에서는 범위(Range) 라이브러리에 다양한 알고리즘들이 추가되어, 데이터 처리 파이프라인을 한층 더 풍부하게 구성할 수 있게 되었습니다. 그중에서도 std::ranges::drop_laststd::ranges::drop_last_while 함수는 범위의 끝부분에서 특정 개수나 조건에 맞는 원소들을 제거(drop) 하는 기능을 제공합니다. 이를 통해 기존의 std::ranges::drop가 범위의 앞부분을 제거하는 것에 더해, 뒷부분에 대해 유사한 조작을 간단하게 수행할 수 있게 되었습니다.

이번 글에서는 std::ranges::drop_last와 std::ranges::drop_last_while의 개념과 사용법, 그리고 이전 방식과 비교하여 어떤 점이 개선되었는지 살펴보겠습니다.

std::ranges::drop_last란 무엇인가요?

  • std::ranges::drop_last(rng, n): 범위 rng의 끝부분에서 n개의 원소를 제거한 새로운 범위를 반환합니다.
  • 예를 들어 [1,2,3,4,5]에서 drop_last(..., 2)를 하면 [1,2,3]을 얻을 수 있습니다.

std::ranges::drop_last_while란 무엇인가요?

  • std::ranges::drop_last_while(rng, pred): 범위 rng를 뒤에서 앞으로 탐색하면서, 조건자 pred를 만족하는 원소를 계속 제거합니다. pred를 만족하지 않는 원소를 만나는 순간 그 앞부분의 범위를 반환합니다.
  • 예를 들어 [10,20,30,40,0,0]에 대해 뒤에서부터 pred(x) = (x == 0) 검사 시, drop_last_while(..., pred)를 하면 뒷부분의 0,0을 제거하여 [10,20,30,40]를 반환합니다.

이전 버전에서는 어떻게 했나요?

C++20까지는 범위의 끝부분에서 원소를 제거하기 위해 다음과 같은 방법이 필요했습니다.

예제: 기존 방식(C++20까지)

#include <vector>
#include <iostream>

// 뒷부분 2개 원소 제거 예
int main() {
    std::vector<int> vec = {1,2,3,4,5};
    // drop_last와 유사한 동작
    // vec.end()-2까지를 서브 범위로 얻거나, resize etc.
    vec.resize(vec.size() - 2); // vec: {1,2,3}

    // 조건 기반 제거 (drop_last_while 유사)
    // 뒤에서부터 조건 만족하는 원소 제거
    while (!vec.empty() && vec.back() == 0) {
        vec.pop_back();
    }

    return 0;
}
  • 문제점:
    • 단순히 뒷부분 원소를 제거하는데 resize나 pop_back 반복 등 별도 로직 필요
    • 조건 기반 제거 시 iterator 또는 pop_back 반복 필요, 함수형 스타일 파이프라인에서 표현하기 어려움

C++23의 std::ranges::drop_last 사용 예제

#include <ranges>
#include <vector>
#include <iostream>

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

    // 뒤에서 2개 원소 제거
    auto dropped = std::ranges::drop_last(vec, 2);

    for (int x : dropped) {
        std::cout << x << ' '; // 출력: 1 2 3
    }

    return 0;
}
  • 한 번의 함수 호출로 뒷부분 n개 원소 제거 가능, 가독성 향상.

C++23의 std::ranges::drop_last_while 사용 예제

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> data = {10,20,30,40,0,0};

    // 뒤에서부터 0인 원소 제거
    auto cleaned = std::ranges::drop_last_while(data, [](int x){ return x == 0; });

    for (int x : cleaned) {
        std::cout << x << ' '; // 출력: 10 20 30 40
    }

    return 0;
}
  • 조건자 하나로 뒤에서 조건을 만족하는 원소들을 제거, 이전에는 pop_back 반복 또는 reverse iterator로 구현해야 했던 로직 단순화.

어떻게 좋아졌나요?

  • 코드 간결화: 범위의 끝부분 처리 로직을 한 번의 함수 호출로 명확하게 표현.
  • 함수형 프로그래밍 스타일 강화: 다른 범위 어댑터(transform, filter, take 등)와 조합하여 데이터 파이프라인에서 뒷부분 정리 로직을 자연스럽게 추가.
  • 가독성 향상: drop_last, drop_last_while라는 이름에서 의도를 명확히 파악, iterator 수동 조작 불필요.
  • 유연성 증가: drop_last_while를 통해 조건 기반 로직 자유롭게 적용, 간단히 조건자 변경으로 새로운 제거 로직 적용.

주의 사항

  • drop_last 시 경계 상황: n이 범위 길이보다 크면 빈 범위 반환. 코드 로직에서 n 값 주의 필요.
  • drop_last_while 시 조건자 정의: 조건자가 참을 반환하는 동안만 뒤에서 제거하므로, 적절한 조건자 로직 필수.
  • C++23 지원 여부: C++23 기능이므로, 지원하는 컴파일러와 표준 라이브러리 필요.

요약

C++23의 std::ranges::drop_last와 std::ranges::drop_last_while는 범위의 끝부분 원소를 제거하는 작업을 간결하고 직관적으로 만듭니다. 이전에는 iterator 조작이나 반복적 pop_back 등의 작업이 필요했지만, 이제는 함수 한 번의 호출로 이 의도를 명확히 구현할 수 있어 코드 가독성과 유지보수성, 생산성이 향상됩니다.

 

참고 자료:

반응형