# Template Alchemy: Mastering Variadic Packs with TypePack (Part 1 of 8)

In modern C++, variadic templates allow us to work with an arbitrary number of template arguments. However, anyone who has delved deep into Template Metaprogramming (TMP) knows that "parameter packs" (the `Ts...` in a template) are quite elusive. They aren't objects, they aren't types, and they aren't quite arrays. They are a language construct that can only be expanded in specific contexts.

To harness the power of these packs, we need a way to "capture" them, move them around, and manipulate them without immediate expansion. This is where `TypePack` comes in.

### The Problem: Why Can't We Just Use Packs?

A pack of types is not a first-class citizen in C++. You cannot store a pack in a variable, nor can you create a `using` alias for a raw pack.

Consider the following illegal code:

```cpp
template <typename... Ts>
struct Error {
    using MyPack = Ts...; // ERROR: A parameter pack cannot be used as an alias
};
```

Because of this limitation, if you want to pass a collection of types to another metafunction or store them for later use, you must wrap them in a **container**.

#### The `std::tuple` Example

The most common example of a pack container in the Standard Library is `std::tuple`.

```cpp
// std::tuple "captures" the pack Ts into a single type
std::tuple<int, double, char> myData; 
```

While `std::tuple` is great for storing *values* of different types, it carries overhead because it’s a concrete class designed for runtime use. For pure compile-time metaprogramming, we need something lighter—a "type-only" container.

### The Implementation: `TypePack`

`TypePack` is a minimalist structure designed to hold a pack of types. It inherits from `std::type_identity` to make it easier to refer to the pack's own type in complex transformations.

```csharp
#include <type_traits>

template <class... Ts>
struct TypePack : std::type_identity<TypePack<Ts...>> {
    // This structure intentionally has no data members.
    // Its only purpose is to carry the pack 'Ts...' in its signature.
};
```

By wrapping `Ts...` inside `TypePack`, the pack becomes part of a single, concrete type. You can now pass this `TypePack` to other templates, nest it, or return it from a metafunction.

### Validation and Examples

To ensure our `TypePack` behaves as expected, we can use `static_assert`. These tests verify that different instances of `TypePack` are recognized as unique types and that they correctly inherit their own identity.

```csharp
// 1. Verify that empty packs are valid
using EmptyPack = TypePack<>;
static_assert(!std::is_same_v<EmptyPack, TypePack<int>>);

// 2. Verify identity inheritance
using MyTypes = TypePack<int, float, double>;
static_assert(std::is_same_v<MyTypes::type, MyTypes>);

// 3. Nested TypePacks (TypePacks can hold other TypePacks)
using Nested = TypePack<TypePack<int, int>, char>;
static_assert(std::is_same_v<Nested::type, TypePack<TypePack<int, int>, char>>);
```

### Conclusion

`TypePack` is the foundation of our library. By wrapping a variadic pack into a struct, we bypass the language limitations that prevent us from treating packs as entities. We have moved from a raw, "unstable" pack to a stable, nameable type.

In the next part, we will explore how to calculate the size of a `TypePack` and how to get individual types from it.
