C++23에서는 범위(Range) 라이브러리를 더욱 풍성하게 만들기 위해 std::ranges::to 함수 템플릿이 도입되었습니다. 이번 글에서는 std::ranges::to의 개념과 사용법, 그리고 이전 버전과 비교하여 어떻게 개선되었는지 알아보겠습니다.
std::ranges::to란 무엇인가요?
C++20에서 범위(Range) 라이브러리가 도입되면서, 파이프라인 형태로 뷰(View)나 어댑터(Adapter)를 조합하여 데이터를 변환, 필터링, 슬라이싱하는 것이 가능해졌습니다. 하지만 변환된 범위를 최종적으로 컨테이너로 재수집하는 과정은 여전히 수동으로 작성해야 했습니다.
C++23의 std::ranges::to는 이러한 변환 결과를 간단한 한 줄의 코드로 원하는 컨테이너로 모아주는 유틸리티 함수 템플릿입니다. 즉, 다양한 뷰나 변환을 거친 범위를 std::vector, std::list 등으로 손쉽게 변환할 수 있습니다.
이전 버전에서는 어떻게 했나요?
C++20 범위 라이브러리를 활용하면 다음과 같은 변환이 가능했습니다.
예제: C++20 방식
#include <vector>
#include <ranges>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 짝수만 필터링하고 제곱한 뒤, 다시 vector로 모으기 (C++20)
auto even_squares_view = numbers
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
// 범위를 다시 vector로 만들기 위해서는 직접 삽입 필요
std::vector<int> result;
std::ranges::copy(even_squares_view, std::back_inserter(result));
for (int v : result) {
std::cout << v << ' ';
}
return 0;
}
- 문제점:
- 범위를 거쳐 나온 결과를 컨테이너로 재수집하기 위해 std::ranges::copy를 사용하거나, 직접 insert iterator를 사용해야 했습니다.
- 한 줄로 간단히 변환하기 어렵고, 중간에 insert iterator나 copy를 작성하는 번거로움이 있었습니다.
C++23의 std::ranges::to를 사용한 개선
std::ranges::to를 사용하면 변환 결과를 원하는 컨테이너 타입으로 직접 변환할 수 있습니다.
예제: std::ranges::to 사용
#include <vector>
#include <ranges>
#include <iostream>
#include <utility> // for std::piecewise_construct if needed
#include <string>
// C++23의 std::ranges::to를 사용하기 위해서는 구현체 지원 필요 (예: libstdc++에서 지원 예정)
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto even_squares = numbers
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; })
| std::ranges::to<std::vector>(); // 결과를 vector<int>로 바로 변환
for (int v : even_squares) {
std::cout << v << ' ';
}
// 출력: 4 16
return 0;
}
- std::ranges::to<std::vector>()를 통해 변환된 범위를 바로 std::vector로 얻을 수 있습니다.
- 코드가 훨씬 간결하고 직관적으로 바뀌었습니다.
어떻게 좋아졌나요?
- 코드 간소화: std::ranges::to를 사용하면 컨테이너 변환 로직을 한 줄로 처리할 수 있습니다.
- 범용성: 다양한 컨테이너(std::vector, std::list, std::string 등)로 변환할 수 있으므로, 코드 재사용성과 유연성이 증대됩니다.
- 가독성 향상: 파이프라인 형태의 변환 후 결과 컨테이너를 얻는 과정이 명확해져 코드 의도를 쉽게 파악할 수 있습니다.
상세한 예제와 비교
1. 다른 컨테이너 타입으로 변환
#include <list>
#include <ranges>
#include <string>
#include <iostream>
int main() {
std::string text = "C++23 Ranges to Container";
auto uppercase_letters = text
| std::views::filter([](unsigned char c) { return std::isalpha(c); })
| std::views::transform([](unsigned char c) { return std::toupper(c); })
| std::ranges::to<std::list>(); // 결과를 list<char>로 변환
for (char c : uppercase_letters) {
std::cout << c;
}
// 출력: CPLUSPLUSRANGESTOCONTAINER
return 0;
}
- std::ranges::to<std::list>()를 사용하여 문자 범위를 std::list<char>로 바로 변환할 수 있습니다.
2. 사용자 정의 컨테이너로 변환 (컨셉 충족 시)
#include <ranges>
#include <iostream>
// 간단한 사용자 정의 컨테이너
template<typename T>
struct MyContainer {
std::vector<T> data_;
void push_back(const T& value) { data_.push_back(value); }
// ... 컨테이너 요구사항 충족 시
};
int main() {
std::vector<int> numbers = {10, 20, 30};
auto result = numbers
| std::views::transform([](int x){ return x+1; })
| std::ranges::to<MyContainer<int>>(); // MyContainer<int>로 변환 가능
for (auto v : result.data_) {
std::cout << v << ' '; // 11 21 31
}
return 0;
}
- std::ranges::to는 컨테이너 요구사항(컨셉)을 만족하는 임의의 타입에도 적용할 수 있어, 유연한 확장성을 제공합니다.
주의 사항
- 컴파일러 및 라이브러리 지원: std::ranges::to는 C++23 기능이므로, 해당 기능을 지원하는 컴파일러와 표준 라이브러리가 필요합니다. (일부 구현체에서 아직 완벽히 지원하지 않을 수 있음)
- 적절한 컨테이너 선택: 변환 대상 컨테이너가 특정 생성자나 삽입 인터페이스를 요구할 수 있으므로, 컨테이너 요구사항을 충족하는지 확인해야 합니다.
요약
C++23의 std::ranges::to는 범위 라이브러리를 통해 변환, 필터링, 조합한 뷰를 손쉽게 원하는 컨테이너로 변환하는 기능을 제공합니다. 이전에는 std::ranges::copy나 삽입 반복자 등을 통해 수동으로 변환해야 했지만, 이제는 한 줄로 컨테이너 변환을 수행할 수 있어 코드 가독성, 유연성, 생산성이 크게 향상됩니다.
참고 자료:
'개발 이야기 > C++' 카테고리의 다른 글
[C++23 새기능 소개] std::optional의 모나딕 연산(transform, transform_or, and_then, or_else) (0) | 2024.12.08 |
---|---|
[C++23 새기능 소개] std::unreachable() 함수 (0) | 2024.12.08 |
[C++23 새기능 소개] [[assume]] 속성 (0) | 2024.12.08 |
[C++23 새기능 소개] std::flat_map과 std::flat_set (0) | 2024.12.08 |
[C++23 새기능 소개] 다차원 첨자 연산자(Multidimensional Subscript Operator) (0) | 2024.12.08 |