[C++23 새기능 소개] std::ranges::starts_with, std::ranges::ends_with, std::ranges::contains

C++23에서는 범위(Range) 라이브러리에 편의성과 가독성을 높이는 유용한 알고리즘들이 추가되었습니다. 특히 std::ranges::starts_with, std::ranges::ends_with, std::ranges::contains 세 가지 함수는 시퀀스를 다룰 때 흔히 필요한 패턴을 간결하고 명확하게 표현할 수 있도록 돕습니다. 이를 통해 문자열이나 컨테이너에서 특정 접두사/접미사 존재 여부나 특정 원소 포함 여부를 직관적으로 확인할 수 있습니다.

 

이번 글에서는 std::ranges::starts_with, std::ranges::ends_with, std::ranges::contains의 개념과 사용법, 그리고 이전 버전과 비교하여 어떤 점이 개선되었는지 알아보겠습니다.

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

  • std::ranges::starts_with(rng, prefix): 범위 rng가 prefix로 시작하는지(접두사인지) 여부를 확인합니다.
  • 예를 들어, 문자열 "Hello World"가 "Hello"로 시작하는지 확인하거나, [1,2,3,4,5]가 [1,2]로 시작하는지 체크하는 데 사용할 수 있습니다.
#include <ranges>
#include <string_view>
#include <iostream>

int main() {
    std::string_view text = "Hello World";
    std::string_view prefix = "Hello";

    if (std::ranges::starts_with(text, prefix)) {
        std::cout << "문자열은 'Hello'로 시작합니다.\n";
    }

    return 0;
}

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

  • std::ranges::ends_with(rng, suffix): 범위 rng가 suffix로 끝나는지(접미사인지) 여부를 확인합니다.
  • 예를 들어, [1,2,3,4,5]가 [4,5]로 끝나는지 확인하거나, "C++23 is great!" 문자열이 "great!"로 끝나는지 검사할 수 있습니다.
#include <ranges>
#include <string_view>
#include <iostream>

int main() {
    std::string_view text = "C++23 is great!";
    std::string_view suffix = "great!";

    if (std::ranges::ends_with(text, suffix)) {
        std::cout << "문자열은 'great!'로 끝납니다.\n";
    }

    return 0;
}

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

  • std::ranges::contains(rng, value): 범위 rng에 특정 값 value가 포함되어 있는지 여부를 확인합니다.
  • 예를 들어 [10,20,30,40]에 30이 포함되어 있는지 손쉽게 체크할 수 있고, 문자열에서 특정 문자가 존재하는지도 확인 가능합니다.
#include <ranges>
#include <vector>
#include <iostream>

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

    if (std::ranges::contains(vec, 30)) {
        std::cout << "30이 벡터에 포함되어 있습니다.\n";
    }

    return 0;
}

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

C++20까지는 이러한 동작을 위해 별도의 알고리즘을 활용하거나, 수동으로 iterator를 탐색하는 로직을 작성해야 했습니다.

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

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

// prefix 검사 예시
bool starts_with(std::string_view text, std::string_view prefix) {
    if (prefix.size() > text.size()) return false;
    return std::equal(prefix.begin(), prefix.end(), text.begin());
}

int main() {
    std::string_view text = "Hello World";
    std::string_view prefix = "Hello";
    if (starts_with(text, prefix)) {
        std::cout << "문자열은 'Hello'로 시작합니다.\n";
    }
    return 0;
}
  • 문제점: starts_with나 ends_with, contains 기능을 구현하기 위해 별도의 함수나 알고리즘 호출 필요, 코드 길어지고 가독성 낮음.

C++23의 개선점

C++23의 std::ranges::starts_with, std::ranges::ends_with, std::ranges::contains를 사용하면 다음과 같은 이점이 있습니다.

  • 코드 간결성: 한 번의 함수 호출로 의도 명확하게 표현 가능.
  • 가독성 향상: 이름에서 바로 목적이 드러나며, 범위 기반이므로 iterator 계산 등 보일러플레이트 코드 불필요.
  • 타입 안정성: 범위의 개념에 따라 동작하므로, 문자열뿐만 아니라 모든 범위(Range) 형태의 컨테이너나 시퀀스에 적용 가능.
  • 함수형 프로그래밍 스타일과의 결합: 다른 뷰 어댑터나 변환 로직과 결합해 복잡한 데이터 파이프라인 중간에서 조건을 손쉽게 검사 가능.

다른 뷰와의 조합 예시

#include <ranges>
#include <iostream>
#include <string_view>

int main() {
    std::string_view text = "Hello C++23 Ranges!";
    auto words = text | std::views::split(' ') | std::views::transform([](auto w) {
        return std::string_view(&*w.begin(), std::ranges::distance(w));
    });

    // words: {"Hello", "C++23", "Ranges!"}
    // "C++23"라는 단어가 포함되어 있나?
    if (std::ranges::contains(words, "C++23")) {
        std::cout << "문장에 'C++23' 단어가 포함되어 있습니다.\n";
    }

    return 0;
}

주의 사항

  • 범위 개념: starts_with, ends_with, contains 모두 범위 개념에 따라 동작하므로, 입력 범위가 조건을 만족하는지 사전에 체크.
  • 비용: 내부적으로 최소한의 비교나 탐색 작업 수행, 성능 요구사항에 따라 주의.
  • C++23 지원 여부: C++23 기능이므로, 해당 기능을 지원하는 컴파일러와 표준 라이브러리 필요.

요약

C++23에서 추가된 std::ranges::starts_with, std::ranges::ends_with, std::ranges::contains는 범위 라이브러리의 편의성과 표현력을 크게 높여주는 함수들입니다. 이전에는 별도 알고리즘이나 수동 구현이 필요했던 접두사/접미사 확인, 원소 포함 여부 검사가 한 번의 함수 호출로 명확하게 표현 가능해졌습니다. 이를 통해 코드 가독성, 유지보수성, 개발 생산성이 모두 개선됩니다.

 

 

참고 자료:

반응형