[모던 C++ #4] 옛날 for 루프를 버리고 range-based for와 C++20 Ranges로!

초창기 C++에서는 컨테이너를 순회하기 위해 전통적인 for 루프나 반복자(iterator)를 명시적으로 사용하는 패턴이 흔했습니다. 이런 방식은 인덱스나 반복자 관리에 번거로움이 있었고, 컨테이너 타입이 바뀌면 루프 조건을 바꾸는 등 유지보수가 어려웠습니다.

모던 C++에서는 이러한 문제를 해소하기 위해 range-based for 루프C++20 Ranges 라이브러리가 등장했습니다. range-based for 루프를 통해 컨테이너 순회를 단순화하고, Ranges 라이브러리를 사용하면 필터링, 변환 등의 파이프라인 스타일 알고리즘을 선언적으로 표현할 수 있습니다. 이로써 코드 가독성과 유지보수성이 크게 향상됩니다.

관련 참고 자료:

과거: 전통적 for 루프와 반복자

C++98/03 시절, 컨테이너 순회는 주로 인덱스 기반 for 루프나 반복자를 명시적으로 사용하는 방식이었습니다.

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1,2,3,4,5};
    for (std::size_t i = 0; i < vec.size(); ++i) {
        std::cout << vec[i] << " ";
    }
    std::cout << "\n";
    return 0;
}

인덱스 관리가 번거롭고, 만약 컨테이너가 std::list로 변경된다면 operator[] 접근 대신 반복자를 사용해야 하는 등 코드 수정이 필요합니다. 또한, 반복자를 사용할 경우에도 매번 begin(), end()를 명시하고 ++it 등 반복자 연산을 수행해야 했습니다.

현재: range-based for 루프와 C++20 Ranges

range-based for 루프

C++11에서 도입된 range-based for 루프는 컨테이너 순회를 매우 단순화합니다.

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1,2,3,4,5};
    // 범위 기반 for 루프를 사용하면 인덱스나 반복자 관리 불필요
    for (int v : vec) {
        std::cout << v << " ";
    }
    std::cout << "\n";
    return 0;
}

컨테이너 타입이 바뀌어도 문법은 동일합니다. 이로써 코드가 더 직관적이 되고, 실수나 버그를 줄일 수 있습니다.

C++20 Ranges를 통한 선언적 스타일

C++20에서는 Ranges 라이브러리가 도입되어, 컨테이너를 필터링하거나 변환하는 과정을 파이프라인 스타일로 표현할 수 있게 되었습니다.

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

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

    // 짝수만 필터링 후 제곱해서 출력
    for (auto v : vec | std::views::filter([](int x){ return x % 2 == 0; })
                      | std::views::transform([](int x){ return x * x; })) {
        std::cout << v << " ";
    }
    std::cout << "\n";
    return 0;
}

위 코드에서는 람다 함수를 통해 필터와 변환을 파이프라인 형태로 자연스럽게 연결하고 있습니다. 이를 통해 "짝수를 골라낸 다음, 각각을 제곱하여 출력한다"는 의도를 코드로 명확하게 표현할 수 있습니다.

왜 이런 변화가 필요한가?

  1. 가독성 향상
    range-based for 루프는 컨테이너를 단순히 "순회"하는 의도를 명확히 표현하고, C++20 Ranges는 필터링, 변환 등 부가적 연산을 선언적으로 드러냅니다.
  2. 유지보수성과 확장성
    컨테이너 타입이 바뀌어도 range-based for 루프는 동일한 문법으로 작동합니다. Ranges는 파이프라인 스타일로 알고리즘을 조합할 수 있으므로, 새로운 요구사항에 맞춰 코드를 쉽게 확장하고 수정할 수 있습니다.
  3. 함수형 프로그래밍 패러다임 지원
    C++20 Ranges는 함수형 프로그래밍 스타일을 받아들여, 람다식과 파이프 연산자를 통해 더 모듈러하고 재사용 가능한 코드를 작성할 수 있게 해줍니다.
반응형