Typelists
Develop an understanding of managing types at compile-time in this lesson.
We'll cover the following...
A typelist (also called type list) is a compile-time construct that enables us to manage a sequence of types. A typelist is somehow similar to a tuple but doesn’t store any data. A typelist only carries type information and is used exclusively at compile-time for implementing different metaprogramming algorithms, type switches, or design patterns such as Abstract Factory or Visitor.
Note: Although both the “type list” and “typelist” spellings are in use, most of the time we will find the term “typelist” in C++ books and articles. Therefore, this will be the form we’ll use in this course.
Typelists were popularized by Andrei Alexandrescu in his book, Modern C++ Design, published a decade before the release of C++11 (and variadic templates). Alexandrescu defined a typelist as follows:
template<class T, class U>struct Typelist{typedef T Head;typedef U Tail;};
In his implementation, a typelist is composed of a head, which is a type, and a tail, which is another typelist. In order to perform various operations on the typelist (which will be discussed shortly), we also need a type to represent the end of the typelist. This can be a simple, empty type that Alexandrescu defined as follows:
class null_typelist {};
Having these two constructs, we can define typelists in the following way:
typedef Typelist<int, Typelist<double, null_typelist>> MyList;
Variadic templates make the implementation of typelists simpler, as shown in the next snippet:
template<typename ... Ts>struct typelist {};using MyList = typelist<int, double>;
The implementation of operations of typelists (such as accessing a type at a given index, adding or removing types from the list, and so on) differs significantly depending on the selected approach. In this course, we’ll only consider the variadic template version. The advantage of this approach is simplicity at different levels: the definition of the typelist is shorter, there’s no need for a type to represent the end of the list, and defining typelist aliases is also shorter and easier to read.
Today, perhaps many of the problems for which typelists represented the solution can also be solved using variadic templates. However, there are still scenarios where typelists are required. Here’s an example: let’s consider a variadic metafunction (a type trait that performs a transformation of types) that does some transformation (such as adding the const
qualifier) to the type template arguments. This metafunction defines a member type that represents the input types and one that represents the transformed types. If we try to define it as follows, it won’t work:
template<typename ... Ts>struct transformer{using input_types = Ts...;using output_types = std::add_const_t<Ts>...;};
This code produces compiler errors because the expansion of the parameter pack is not possible in this context. This ...