Когда я решил разобраться с вариадик темплейтами, то, как обычно, пошел искать статьи на хабре и нашел их великое множество, но почти все они либо написаны так, как будто ты уже собаку съел на всех этих эллипсисах и тебе ничего не надо пояснять, либо не содержали нужной мне информации. Но вот наткнулся на эту статью, где все вроде бы понятно, рассмотрена концепция "откусывания головы" у переменного списка параметров (эллипсис), потом специализацию "дна", когда параметров уже не осталось и создается пустая структура (что есть конец шаблонной рекурсии) и аналогично, когда мы перечисляем все поля, реализован шаблон для нулевого поля ("стукаемся о дно структуры"). И для демонстрации всего этого рассмотрен пример реализации tuple. Но когда попробовал реализовать все это на VS2022, то выяснилось, что пример из хабра какой-то, мягко говоря, неоптимальный, когда на три поля (int, double, int) уходит 56 байт, а на обычную структуру уходит всего лишь 24 байта (это, кстати, при сумме размеров полей 16 байт, но тут уже выравнивание по 8 байтам уже работает на 64-битной архитектуре), что тоже не идеально, но уже гораздо лучше. Я собрал пример из статьи на хабре и получил вот такой отутпут:
12 2.34 89
12 2.34 89
12 2.34 89
sizeof(int) + sizeof(double) + sizeof(int) = 16
sizeof(habr_tuple<int, double, int>) = 56
sizeof(plain_struct) = 24
- template<typename... Args>
- struct habr_tuple;
- template<typename Head, typename... Tail>
- struct habr_tuple<Head, Tail...> : habr_tuple<Tail...>
- {
- habr_tuple(Head h, Tail... tail)
- : habr_tuple<Tail...>(tail...), head_(h)
- {
- }
- typedef habr_tuple<Tail...> base_type;
- typedef Head value_type;
- base_type base = static_cast<base_type&>(*this);
- Head head_;
- };
- template<>
- struct habr_tuple<>
- {
- };
- template<int I, typename Head, typename... Args>
- struct getter
- {
- typedef typename getter<I - 1, Args...>::return_type return_type;
- static return_type get(habr_tuple<Head, Args...> t)
- {
- return getter<I - 1, Args...>::get(t);
- }
- };
- template<typename Head, typename... Args>
- struct getter<0, Head, Args...>
- {
- typedef typename habr_tuple<Head, Args...>::value_type return_type;
- static return_type get(habr_tuple<Head, Args...> t)
- {
- return t.head_;
- }
- };
- template<int I, typename Head, typename... Args>
- typename getter<I, Head, Args...>::return_type
- get(habr_tuple<Head, Args...> t)
- {
- return getter<I, Head, Args...>::get(t);
- };
- template<typename... Args>
- struct my_tuple;
- // отделяем хвост от головы списка параметров
- template<typename Head, typename... Tail>
- struct my_tuple<Head, Tail...>
- {
- my_tuple(Head h, Tail... tail)
- : head_(h), tail_(tail...)
- {
- }
- typedef Head value_type;
- Head head_;
- my_tuple<Tail...> tail_;
- };
- // нет параметров - конец рекурсии (пустая структура)
- template<>
- struct my_tuple<>
- {
- };
- // вспомогательная функция для получения поля по номеру
- template<int I, typename Head, typename... Args>
- struct getter2
- {
- typedef typename getter2<I - 1, Args...>::return_type return_type;
- static return_type get(my_tuple<Head, Args...> t)
- {
- return getter2<I - 1, Args...>::get(t.tail_);
- }
- };
- // вспомогательная функция, специализация для 0 (конец рекурсии)
- template<typename Head, typename... Args>
- struct getter2<0, Head, Args...>
- {
- typedef typename my_tuple<Head, Args...>::value_type return_type;
- static return_type get(my_tuple<Head, Args...> t)
- {
- return t.head_;
- }
- };
- // вспомогательная ф-я
- template<int I, typename Head, typename... Args>
- typename getter2<I, Head, Args...>::return_type
- get(my_tuple<Head, Args...> t)
- {
- return getter2<I, Head, Args...>::get(t);
- };
Это уже гораздо лучше, а теперь померяем размеры всех этих структур, сравним с std::tuple и обычной структурой: