본문 바로가기

진리는어디에/C++

[C++] std::integer_sequence

들어가며

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);
}

 

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!