Как мне создать динамический список в метапрограммировании шаблонов?

#c #list #templates #variadic-templates

#c #Список #шаблоны #переменные-шаблоны

Вопрос:

Я пробовал метапрограммирование шаблонов, и у меня возникли некоторые проблемы с созданием динамического списка. Я пытался

 #include <iostream>
template<int I>
struct Int {

};
template<class _Value, class ..._Others>
struct List {
    typedef _Value Value;
    typedef List<_Others...> Next;
};
template<class _Value>
struct List<_Value, void> {
    typedef _Value Value;
    typedef void Next;
};

template<class _List>
void PrintList() {
    std::cout << typename _List::Value::I << "n";
    PrintList<typename _List::Next>();
};
template<>
void PrintList<void>() {};

int main() {
    PrintList<List<Int<1>, Int<2>, Int<3>>>();
}
  

Но я получаю 2 ошибки компиляции, которые я не могу понять. Первый expected '(' before '<<' token включен std::cout << typename _List::Value::I << "n"; . Я не могу понять, как распечатать значение int (я тоже пробовал (typename _List::Value)::I ).

Вторая ошибка заключается в том, что у меня неверное количество аргументов шаблона в typedef List<_Others...> Next; . Не следует ли ограничить первый аргумент Value , а остальные Others ?

Комментарии:

1. Здесь есть две отдельные ошибки компиляции с совершенно разными первопричинами, которые не имеют ничего общего друг с другом и будут иметь разные ответы. Один вопрос на stackoverflow.com вопрос, пожалуйста. Можете ли вы ограничить свой вопрос ровно одним вопросом?

Ответ №1:

Вы не можете получить доступ к параметрам шаблона, используя :: синтаксис. Обычным соглашением является добавление type using для типов и value constexpr static членов для значений.

 template<int I>
struct Int {
   constexpr static auto value=I;
};
  

Затем используйте
std::cout << _List::Value::value << "n"; . Это не тип, поэтому помещение typename туда также было неправильным.

Вторая ошибка заключается в том, что вы определили, List имея хотя бы один аргумент, но вы возвращаетесь к List<_Others...> , который в конце пуст. Даже если вы хотели исправить это с помощью void , вы забыли добавить его в список, попробуйте PrintList<List<Int<1>, Int<2>, Int<3>,void>>();

Но наличие контрольного значения необязательно, смотрите это:

 #include <iostream>
template<int I>
struct Int {
    static constexpr auto value=I;
};
//Define List as naturally having any number of arguments, even zero.
template<class...Ts>
struct List{};

//Non-empty specialization.
template<class Head, class...Tail>
struct List<Head,Tail...>{
    using head = Head;
    using tail = List<Tail...>;
};

template<class _List>
void PrintList() {
    std::cout <<  _List::head::value << "n";
    PrintList<typename _List::tail>();
};
//Printing empty list.
template<>
void PrintList<List<>>() {};

int main() {
    PrintList<List<Int<1>, Int<2>, Int<3>>>();
}
  

Лучше использовать using на C , они более удобочитаемы и могут быть шаблонизированы.

В C 17 вы можете использовать выражения fold:

 template<typename...Elements>
void fold_print(List<Elements...>){
    ((std::cout<<Elements::value<<'n'),...);
}
int main() {
    fold_print(List<Int<1>, Int<2>, Int<3>>{});
}
  

Но функции могут распаковывать только свои аргументы, поэтому синтаксис немного отличается. Или для этого потребуется еще одна struct оболочка, которая их распаковывает.