C++ template - 4.1 Pack Indexing

JunTak Lee·2024년 4월 3일
0

C++ Template

목록 보기
5/8

Compiler Support C++26 feature을 보던중에 재미난놈이 있어서 들고왔다
C++26 standard가 이제 시작하는 마당에 이걸 지금 소개하는게 맞나 싶지만은,
일단 clang이 19에 넣겠다고 선언한걸 보면 들어가긴 할려는 모양이다

이번편도 제목에서 알 수 있듯이 번외편에 해당한다
사실 추가적인 기능이라기 보다는 Syntactic Suger에 해당하는 문법이기 때문이다
어찌되었건 이놈도 계륵같은 존재이기는 하나, 알아두면 편한건 맞아서 소개하려한다


Before C++26

이 문법이 등장하게된 배경을 살펴보려면, 기존에 사용하던 문법을 봐야한다
바로 지난글에서 설명한 내용중 Expansion에 해당되는데, 다시 들고와보았다
작성 시점이 거의 1년이 지난시점이라 기억이 안난다..

#include <iostream>

template <typename T>
void foo(T arg) {
    std::cout << typeid(T).name() << std::endl;
}

template <typename _1, typename... Ts>
void foo(_1 arg, Ts... args) {
    std::cout << typeid(arg).name() << " ";
    foo<Ts...>(args...);
}

int main() {
    foo<int, float, double>(0, 0.f, 0.);
}

// Output
i f d

기존의 Parameter Pack에서는 Recursion을 이용했다
따라서 저 중 단 한개의 element만 접근하려해도 저 긴 문법을 작성해야했다
물론 meta-programming을 없애기 위한 노력으로 많이 단순화되긴 했지만 그래도 상당히 복잡하다
추가적으로 parameter pack에서 type을 뽑아내려하면 더 골머리가 아팠다

#include <iostream>

template <typename... Ts>
struct pack_expansion {
    template <typename _1, typename... _Ts>
    struct pack_expansion_impl : pack_expansion_impl<_Ts...> {
        pack_expansion_impl() {
            std::cout << typeid(_1).name() << " ";
        }
    };

    template <typename T>
    struct pack_expansion_impl<T> {
        pack_expansion_impl() {
            std::cout << typeid(T).name() << " ";
        }
    };

    pack_expansion_impl<Ts...> expansion;
};

int main() {
    pack_expansion<int, float, double> p;
}

// Output
d f i

이러한 문제를 해결하기 위해 Fold Expression이 등장했다
허나 Fold Expression은 단지 iteration을 위한 기능일뿐이다
따라서 element 단위로 접근하기 위해서는 여전히 저 복잡한 TMP를 사용해야했다


Indexing Function Parameter

이러한 문제를 해결하기 위해 C++26에 Pack Indexing이란 문법이 등장한다
https://en.cppreference.com/w/cpp/language/pack_indexing

우선 function parameter 접근이 어떻게 변경되었는가를 살펴보자

#include <print>

template <typename... Args>
void foo(Args... args) {
    std::print("{}", args...[1]);
}

auto main() -> int {
    foo(1, 2, 3, 5);
}

길고 난해한 SFINE를 symbol 5글자(?)로 대체해버렸다
이걸 좀더 Generic하게 작성해주면 다음과 같다

#include <cstddef>
#include <utility>

template <size_t N, typename... Args>
    requires (sizeof...(Args) > N)
constexpr decltype(auto) get(Args&&... args) {
    return std::forward<Args...[N]>(args...[N]);
}

auto main() -> int {
    return get<1>(1, 2, 3, 5);
}

// Output
2

이처럼 parameter pack을 마치 array처럼 사용할 수 있게 되었다


Indexing Template Parameter

사실 바로 위 예제 std::forward template argument로 사용하긴했다
눈치 못챘을수도 있는데, 문법이 똑같기 때문이다
단지 뭘 indexing하냐에 따른 차이일 뿐이다

CppReference에서 소개한 예시를 보면 위와같은 함수도 필요없고 type alias로 가능하다

#include <cstddef>

template <size_t N, typename... Args>
using get_type = Args...[N];

auto main() -> int {
    return get_type<1, float, int, double>(5);
}

뭔가 예시를 잘못만든거 같긴한데 어찌되었건 type int를 얻을 수 있다
무엇보다 중요한 점은 기존의 길고긴 TMP를 단 두줄로 함축시켜버릴 수 있다는 것이다
추가적으로 코드를 읽기도 굉장히 수월해졌다고 볼 수 있다

그렇다면 template template parameter에서는 어떻게 적용할 수 있을까
type alias를 하나 더 만들어서 해결할 수도 있겠지만 좀 더 Generic한걸 만들고 싶었다
이 경우에는 다소 Old한 방법을 섞으면 달성할 수 있다

#include <cstddef>
#include <tuple>

template <size_t N, typename... Args>
struct get_type;

template <size_t N, typename... Args>
    requires (sizeof...(Args) > N)
struct get_type<N, Args...> {
    using type = Args...[N];
};

template <
    size_t N, 
    template <typename...> typename T, 
    typename... Args
> requires (sizeof...(Args) > N)
struct get_type<N, T<Args...>> {
    using type = Args...[N];
};

template <size_t N, typename... Args>
using get_type_t = get_type<N, Args...>::type;

auto main() -> int {
    std::tuple t{2.0f, 1, 3.0};
    return get_type_t<1, decltype(t)>(5);
}

이렇게 하면 tuple의 element type도 별다른 도움 없이 가져올 순 있다


처음봤을때는 Parameter Pack을 쉽게 바꿔주리라 믿었다
그런데 막상 사용하려고하니 너무 애매해서 사용할 일이 있을까 싶다..
아마도 Reflection TS의 tier 2에 언급된걸 보면, Reflection과 관련있지 않을까 싶다
좀더 찾아보고 고민해보고 싶은데, 시간이 허락해주지 않아 여기서 끝내려한다

그나저나 C++20 부터 굉장히 많은 Syntactic Suger가 첨가되고 있는데 괜찮은건지 모르겠다
최근 C#의 Syntactic Suger를 대량으로 주입당해서 개인적으로 좋긴한데 뭔가 찜찜하다
내가 알고 먹던 C++이라기 보다는 C ~ C#까지의 문법적인 변화과정을 보는것 같다
최근 몇년간의 CppCon 영상들에서 C# 문법들이 자주 보이는건 기분탓이라고 믿고싶다

요즘 대세가 Syntactic Suger를 대량으로 첨부해서 MZ한 언어를 만드는거 같던데, 이러다가 당뇨오는거 아닌지 모르겠다..

0개의 댓글