들어가며
std::integer_sequence는 C++ 표준 라이브러리 클래스로써 다음과 같은 특징을 가진다.
- 컴파일 타임에 정수 집합을 제공
- 함수 템플릿의 인자로 사용 될 때, 파라미터 팩 확장에 사용될 수 있음.
팩 확장에 대한 자세한 내용은 [여기] 참조
오늘 포스트는 이런 std::integer_sequence의 특징을 활용하는 방법에 대해 살펴 보도록 한다.
우리에게 아래와 같은 튜플 객체가 있다고 가정하자.
std::tuple<int, double, char> t(1, 3.14, 'A');
이제 튜플에 있는 모든 요소들을 출력한다고 해보자. 간단히 다음과 같은 코드를 생각해 볼 수 있다.
std::cout << std::get<0>(t) << ", " << std::get<1>(t) << ", " << std::get<2>(t) << std::endl;
하지만 매번 저렇게 각 요소마다 상수를 써가며 출력 코드를 만들 수는 없다.
그래서 다음과 같이 print_tuple 함수를 만들어, 튜플의 요소들을 순회하면서 출력 하도록 해보자.
template <class Tuple, class...Args>
void print_tuple(Tuple& t, Args ... idx)
{
// idx 파라메터 팩 안에 들어 있는 것들은. 변수. 컴파일 타임 상수가 아니다.
std::cout << std::get<idx>(t) << std::endl; // 컴파일 에러
}
std::tuple<int, double, std::string> t{ 42, 3.14, "Hello, World!" };
print_tuple(t, 0, 1, 2);
얼핏 보면 그럴듯해 보이지만, idx 파라미터 팩 안에 있는 값들은 상수가 아닌 변수다. 변수는 템플릿 인자로 사용될 수 없으므로 위 코드는 컴파일 에러가 발생한다.
이제 위의 std::integer_sequence의 특징을 다시 상기해보자. std::integer_sequence는 '컴파일 타임'에 연속적인 정수 집합을 제공한다고 했다. 이제 단순 가변인자를 std::integer_sequence로 바꿔 보자.
template <class Tuple, std::size_t...Idx>
void print_tuple(Tuple& t, std::integer_sequence<std::size_t, Idx...> arg)
{
// idx pack에 있는 요소는 컴파일 상수
// std::cout << std::get<Idx>(t) << std::endl; 이렇게 쓰면 Idx 파라메터 팩은 확장 되어야 한다고 에러를 냄
((std::cout << std::get<Idx>(t) << std::endl), ...); // 폴드 익스프레션으로 사용
}
print_tuple(t, std::index_sequence<0, 1, 2>{});
이제 기본은 만들어졌다. std::index_sequence에 모든 인덱스를 다 넣을 수 없으니, 0 부터 N-1의 연속적인 정수를 자동으로 넣어 주는 make_index_sequence를 이용해 코드를 좀 더 깔끔하게 만들어 보자.
print_tuple(t, std::make_index_sequence<3>{});
튜플 크기가 변경 될 때 마다 상수를 수정할 수 없으니 컴파일 타임에 튜플에서 사이즈를 얻어 올 수 있는 코드를 추가해보자.
print_tuple(t, std::make_index_sequence<std::tuple_size_v<decltype(t)>>{});
다시 코드가 너저분해졌다.
사실 튜플 객체를 이용한다는 것 말고는 다른 요소들은 모두 컴파일 타임에 자동으로 알아 낼 수 있다. 요 부분들을 감싸는 함수를 이용해 이쁘게 다시 꾸며 보자.
C++14에서 표준에 추가
template<class Tuple, std::size_t ... IDX>
void print_tuple_impl(Tuple& tp, std::index_sequence<IDX...> idx)
{
((std::cout << std::get<IDX>(tp) << std::endl), ...); // fold expression
}
template <class Tuple>
void print_tuple(Tuple& tp)
{
print_tuple_impl(tp, std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
int main()
{
std::tuple<int, double, std::string> t{ 42, 3.14, "Hello, World!" };
print_tuple(t);
}