Реализация целочисленных последовательностей C++

Я читал о последовательностях времени компиляции, и мне трудно понять, как работает программа ниже. Может ли кто-нибудь подробно объяснить программу ниже? Спасибо!

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

Пожалуйста, добавьте свои результаты к своему вопросу, чтобы люди могли видеть, где вы допустили ошибку.

Tarick Welling 19.06.2019 17:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
1
390
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Обратите внимание, что это недопустимый синтаксис С++, а скорее просто «графическое представление», предназначенное для демонстрации рекурсивного процесса.

Другие вопросы по теме