Я читал о последовательностях времени компиляции, и мне трудно понять, как работает программа ниже. Может ли кто-нибудь подробно объяснить программу ниже? Спасибо!
#include<iostream>
template<int...>
struct ints
{
};
template<int N, int...args>
struct rev_seq : rev_seq<N - 1, 2 * args..., 1> {};
template<int...args>
struct rev_seq<0, args...>
{
using type = ints<args...>;
};
template<int N>
using RS = typename rev_seq<N + 1>::type;
template<int... args>
void fU(ints<args...>&& s)
{
for (const auto& i : { args... }) std::cout << i << " ";
std::cout << std::endl;
}
int main()
{
fU(RS<5>());
}





RS<2>() создает rev_seq<2, 2>::type
rev_seq<2, 2>::type — основной шаблон (а не специализированный) для rev_seq:
template<int C, int N, int... Is>
struct rev_seq : rev_seq<C - 1, N, N - C, Is...>{}
Это рекурсивное объявление, поэтому оно происходит от такой версии самого себя:
rev_seq<2, 2, (empty int... Is pack)>
происходит от
rev_seq<2-1, 2, 2 - 2>
что такое rev_seq<1, 2, 0>
Этот 0 в конце является частью набора int... Is в базовом классе.
Это снова рекурсия
rev_seq<1, 2, 0>
происходит от
rev_seq<1-1, 2, 2-1, 0>
что такое rev_seq<0, 2, (1, 0)>
Видите, как последний аргумент параметра добавляется к пакету?
rev_seq<0, 2, (1, 0)> соответствует следующему шаблону специализация для rev_seq:
template<int N, int... Is>
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
Обратите внимание, что этот struct не происходит ни от чего
В этот момент тип type в классе становится
ints<2, 1, 0>
Видите, как специализация заставляет нас добавлять N в начало последовательности?
Наконец, мы передаем построенный ints в функцию:
template<int... Is>
void fU(ints<Is...>&& s)
{
for (auto i : { Is... }) std::cout << i << " ";
std::cout << std::endl;
}
Цикл перебирает целые числа
for (auto i : { Is... })
Здесь { Is...} — это расширение пакета, создающее список инициализаторов, который мы можем перебирать. Интересно отметить, что это одно из очень немногих мест, где вы можете просто создать и использовать список инициализаторов, почти все другие случаи сводятся к сопоставлению перегрузки конструктора std::initializer_list для некоторого класса (например, std::vector)
Предполагая, что вы уже знакомы с теорией, лежащей в основе variadic template, лучший подход, который я могу предложить, — «вручную развернуть» определения шаблонов.
В большинстве случаев вариативные шаблоны используют рекурсивный подход.
Попробуем выполнить это упражнение.
Основная часть: RS<5>(). Это просто пример rev_seq<5, 5>::type. Что ж, давайте углубимся в rev_seq.
Его декларация:
template<int C, int N, int... Is>
struct rev_seq // ...
Таким образом, этот экземпляр будет «сопоставлен»:
template<5, 5, $>
struct rev_seq // ...
где $ — это просто символ-заполнитель, указывающий на пустой список с переменным числом переменных.
rev_seq наследуется рекурсивно:
template<5, 5, $>
struct rev_seq : rev_seq <4, 5, 0, $> {}
Конечно rev_seq <4, 5, 0, $> (то есть rev_seq<4, 5, {0}>) наследует и так далее.
<5, 5, $> ->
<4, 5, {0}> ->
<3, 5, {1, 0}> ->
<2, 5, {2, 1, 0}> ->
<1, 5, {3, 2, 1, 0}> ->
<0, 5, {4, 3, 2, 1, 0}>
Когда первый параметр шаблона будет 0, мы остановимся. Потому что в этом случае у нас есть частичная специализация шаблона.
Здесь вы можете увидеть аналогию с "базовый вариант" в стратегии рекурсии.
Таким образом, мы окончательно получаем это наследство:
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
В твоем случае:
struct rev_seq<0, 5, {4, 3, 2, 1, 0}>
{
using type = ints<5, {4, 3, 2, 1, 0}>;
};
Обратите внимание, что ints — это просто вариативный список. То есть: ints<5, {4, 3, 2, 1, 0}> на самом деле есть ints<{5, 4, 3, 2, 1, 0}>.
Итак, в конце концов, вы просто вызываете «функцию печати» с этим конкретным экземпляром ints:
template<{5, 4, 3, 2, 1, 0}>
void fU(ints<{5, 4, 3, 2, 1, 0}>&& s)
{
for (auto i : { 5, 4, 3, 2, 1, 0 }) std::cout << i << " ";
std::cout << std::endl;
}
Обратите внимание, что это недопустимый синтаксис С++, а скорее просто «графическое представление», предназначенное для демонстрации рекурсивного процесса.
Пожалуйста, добавьте свои результаты к своему вопросу, чтобы люди могли видеть, где вы допустили ошибку.