#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
оболочка, которая их распаковывает.