I've been trying template metaprogramming and have had some trouble making a dynamic list. I've tried
#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>>>();
}
But I get 2 compile errors that I can't figure out. The first is expected '(' before '<<' token
on std::cout << typename _List::Value::I << "\n";
. I can't figure out how to print out the int value (I've also tried (typename _List::Value)::I
).
The second error is that I have the wrong number of template arguments at typedef List<_Others...> Next;
. Shouldn't the first argument be capped into Value
, and the rest Others
?
You cannot access template parameters using ::
syntax. The usual convention is to add type
using
for types and value
constexpr static
member for values.
template<int I>
struct Int {
constexpr static auto value=I;
};
Then use
std::cout << _List::Value::value << "\n";
. It is not a type, so putting typename
there was also incorrect.
The second error is because you defined List
having at least one argument, yet you recurse into List<_Others...>
which is empty at the end. Even though you wanted to fix it with void
, you forgot to add it to the list, try PrintList<List<Int<1>, Int<2>, Int<3>,void>>();
But having a sentinel value is not necessary, see this:
#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>>>();
}
It is better to use using
in C++, they are more readeable and can be templated.
In C++17 you can use fold expressions:
template<typename...Elements>
void fold_print(List<Elements...>){
((std::cout<<Elements::value<<'\n'),...);
}
int main() {
fold_print(List<Int<1>, Int<2>, Int<3>>{});
}
But functions can unpack only their arguments, so the syntax is slighlty different. Or it would require one more struct
wrapper that unpacks them.