[C++20 새기능 소개] 범위 (Ranges) 라이브러리

C++20의 새로운 기능들을 소개하는 시리즈의 세 번째 글에 오신 것을 환영합니다. 이번 글에서는 코드의 가독성과 효율성을 높여줄 범위(Ranges) 라이브러리에 대해 자세히 알아보겠습니다.

Ranges란 무엇인가요?

C++20에서 도입된 Ranges 라이브러리는 기존의 반복자(iterator) 기반 알고리즘을 개선하여, 보다 선언적이고 직관적인 방식으로 시퀀스 데이터를 처리할 수 있게 해줍니다. 이는 코드의 가독성을 높이고, 복잡한 데이터 처리 작업을 간단하게 표현할 수 있도록 도와줍니다.

왜 Ranges를 사용해야 할까요?

기존의 STL 알고리즘은 반복자를 사용하여 데이터의 시작과 끝을 지정해야 했습니다. 이는 코드가 장황해지고 가독성이 떨어지는 원인이 되었습니다. Ranges를 사용하면 컨테이너 자체를 알고리즘에 전달하거나, 파이프라인 형태로 여러 연산을 연결할 수 있어 코드가 더욱 간결하고 이해하기 쉬워집니다.

간단한 예제

기존 방식의 코드

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

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

    std::copy_if(vec.begin(), vec.end(), std::back_inserter(result), [](int n) {
        return n % 2 == 0;
    });

    std::sort(result.begin(), result.end(), std::greater<>());

    for (int n : result) {
        std::cout << n << " ";
    }
    // 출력: 4 2

    return 0;
}

위 코드에서는 벡터에서 짝수만 추출하고 내림차순으로 정렬한 후 출력하고 있습니다.

Ranges를 사용한 개선

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

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

    auto result = vec
        | std::views::filter([](int n) { return n % 2 == 0; })
        | std::views::reverse;

    for (int n : result) {
        std::cout << n << " ";
    }
    // 출력: 4 2

    return 0;
}

Ranges를 사용하면 위와 같이 파이프라인 스타일로 코드를 작성할 수 있어 가독성이 크게 향상됩니다.

Ranges의 주요 구성 요소

1. Views

Views는 기존 컨테이너의 데이터를 변환하거나 필터링하여 새로운 시퀀스를 제공하지만, 실제로 데이터를 복사하지 않습니다. 이는 메모리 효율성을 높이고, 지연 평가(lazy evaluation)를 통해 성능을 개선합니다.

예시:

  • std::views::filter: 조건에 맞는 요소만 선택
  • std::views::transform: 요소를 변환
  • std::views::take: 처음 N개의 요소를 선택

2. Actions (C++23에서 도입)

Actions는 Views와 달리 즉시 연산을 수행하여 새로운 컨테이너를 생성합니다. 하지만 C++20에서는 Actions가 아직 표준에 포함되지 않았으므로, 여기서는 언급만 하겠습니다.

3. Adaptors

Adaptors는 Views나 Actions를 조합하거나 변환하여 새로운 동작을 수행할 수 있게 해줍니다.

자세한 예제

복잡한 파이프라인 처리

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

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

    auto result = vec
        | std::views::filter([](int n) { return n % 2 != 0; })
        | std::views::transform([](int n) { return n * n; })
        | std::views::take(3);

    for (int n : result) {
        std::cout << n << " ";
    }
    // 출력: 1 49 9

    return 0;
}

위 코드에서는 다음과 같은 작업을 수행합니다:

  1. 홀수만 필터링
  2. 각 요소를 제곱
  3. 처음 3개의 요소 선택

문자열 처리

#include <ranges>
#include <string>
#include <iostream>

int main() {
    std::string text = "C++20 Ranges are powerful!";

    for (char c : text | std::views::filter([](char ch) { return !std::isspace(ch); })) {
        std::cout << c;
    }
    // 출력: C++20Rangesarepowerful!

    return 0;
}

공백 문자를 제거하여 문자열을 출력하는 예제입니다.

Ranges의 장점

  • 가독성 향상: 파이프라인 스타일로 코드를 작성하여 로직을 쉽게 이해할 수 있습니다.
  • 메모리 효율성: Views는 데이터 복사를 하지 않으므로 메모리 사용량이 적습니다.
  • 지연 평가: 필요한 시점까지 계산을 연기하여 성능을 개선합니다.
  • 모듈성 증가: 작은 단위의 연산을 조합하여 복잡한 작업을 수행할 수 있습니다.

주의 사항

  • Ranges를 사용하기 위해서는 컴파일러와 표준 라이브러리가 C++20을 지원해야 합니다.
  • 일부 Views는 상태를 유지하므로, 복잡한 파이프라인에서는 주의가 필요합니다.

결론

C++20의 Ranges 라이브러리는 시퀀스 데이터를 처리하는 방식을 혁신적으로 개선하여, 개발자들이 더욱 간결하고 효율적인 코드를 작성할 수 있게 해줍니다. Ranges를 활용하여 코드의 가독성과 성능을 모두 잡아보세요.

반응형