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

In the previous chapters, we focused on "positional" operations: doing things based on indices and ranges. However, in real-world metaprogramming, we often care about **what** a type is, not just **where** it is. For example, you might want to strip all `void` types from a pack or replace `float` with `double` for higher precision across a whole interface.

In this article, we will implement type-based filtering and transformation using `remove_t` and `replace_t`.

### Advancing the Interface

We add two more aliases to our `TypePack`. These allow us to perform "search-and-destroy" or "search-and-replace" operations across the entire collection of types.

```cpp
template <class... Ts>
struct TypePack : std::type_identity<TypePack<Ts...>> {
    // ... previous definitions ...

    // Remove all occurrences of type T
    template <class T>
    using remove_t = details::TypePackRemove<T, TypePack>::type;

    // Replace all occurrences of T with TReplacement
    template <class T, class TReplacement>
    using replace_t = details::TypePackReplace<T, TReplacement, TypePack>::type;
};
```

### Type-Based Removal

The `TypePackRemove` utility uses recursive pattern matching to scan the pack. Unlike positional removal, this operation visits every element and decides whether to keep it or discard it.

#### The Logic:

1. **Base Case**: Removing from an empty pack results in an empty pack.
    
2. **Match Case**: If the head of the pack matches the target type `T`, we discard it and continue with the rest of the pack.
    
3. **No-Match Case**: If the head doesn't match, we keep it (using `TypePackInsertAtFirstPosition`) and recurse into the tail.
    

```cpp
template <class T, class Pack>
struct TypePackRemove;

template <class T>
struct TypePackRemove<T, TypePack<>> : std::type_identity<TypePack<>> {
};

template <class T, class...Ts>
struct TypePackRemove<T, TypePack<T, Ts...>> : TypePackRemove<T, TypePack<Ts...>> {
};

template <class T, class TFirst, class...Ts>
struct TypePackRemove<T, TypePack<TFirst, Ts...>> :
    TypePackInsertAtFirstPosition<TFirst, typename TypePackRemove<T, TypePack<Ts...>>::type> {
};
```

### Universal Replacement

`TypePackReplace` follows a similar recursive structure but with a twist: instead of discarding a matching type, we swap it for a new one.

#### The Logic:

* If `Head == Target`, the new pack starts with `Replacement`.
    
* If `Head != Target`, the new pack starts with the original `Head`.
    
* Repeat until the pack is empty.
    

```cpp
template <class T, class TReplacement, class Pack>
struct TypePackReplace;

template <class T, class TReplacement>
struct TypePackReplace<T, TReplacement, TypePack<>> : std::type_identity<TypePack<>> {
};

template <class T, class TReplacement, class...Ts>
struct TypePackReplace<T, TReplacement, TypePack<T, Ts...>> :
    TypePackInsertAtFirstPosition<TReplacement, typename TypePackReplace<T, TReplacement, TypePack<Ts...>>::type> {
};

template <class T, class TReplacement, class TFirst, class...Ts>
struct TypePackReplace<T, TReplacement, TypePack<TFirst, Ts...>> :
    TypePackInsertAtFirstPosition<TFirst, typename TypePackReplace<T, TReplacement, TypePack<Ts...>>::type> {
};
```

### Validation with `static_assert`

Our tests demonstrate that these operations work globally—affecting multiple occurrences of the same type throughout the pack.

```cpp
TEST(TypePackTests, RemoveType)
{
    using Pack = TypePack<int, long, int, double, int>;
    
    // Removes ALL 'int' instances
    using Result = Pack::remove_t<int>;
    static_assert(std::is_same_v<Result, TypePack<long, double>>);
    
    // If the type isn't there, the pack remains unchanged
    static_assert(std::is_same_v<Pack::remove_t<char>, Pack>);
}

TEST(TypePackTests, ReplaceType)
{
    using Pack = TypePack<int, long, int>;
    
    // Replaces ALL 'int' with 'unsigned'
    using Result = Pack::replace_t<int, unsigned>;
    static_assert(std::is_same_v<Result, TypePack<unsigned, long, unsigned>>);
}
```

### Conclusion

By moving from positional to type-based manipulation, we've given our `TypePack` the ability to "understand" its contents. These recursive patterns are the engine behind powerful library features, such as filtering out unsupported types or automatically upgrading types in a template-heavy API.

In the next part, we will look at how to handle nested structures by implementing an operation to **flatten** a pack
