Alias Templates & Template Parameters

Modern C++ C++11 / 14 / 17 Type Safety

Modern C++ offers powerful mechanisms for defining highly readable, type-safe generic code. Alias templates provide a seamless way to name families of types, while the rich variety of template parameters (types, non-types, and template-template parameters) allows developers to shift logic to compile-time, boosting both performance and safety. {: .fs-5 .fw-300 }

Table of Contents

  1. Alias Templates & Template Parameters
    1. Table of Contents
    2. 1. Alias Templates: Intuitive Type Naming
      1. Partially Bound Templates
      2. The C++14 Type-Traits Helpers
    3. 2. The Three Flavors of Template Parameters
      1. Type Parameters
      2. Non-Type Parameters (Compile-Time Values)
      3. Template Template Parameters
    4. 3. Architectural Perspective: Why This Matters

1. Alias Templates: Intuitive Type Naming

Introduced in C++11, alias templates use the using keyword to give a convenient name to a family of types. Unlike traditional typedefs, alias templates can be partially bound, making them an incredible tool for expressing architectural intent directly in code.

Partially Bound Templates

Consider a generic Matrix class with three dimensions of customization: the underlying type, the number of lines, and the number of columns.

template <typename T, int Line, int Col>
class Matrix {
    // ... matrix implementation ...
};

If we want to represent special cases—like a Square (lines equal columns) or a Vector (a single column)—we can use alias templates to partially bind the original parameters.

// (1) A Square matrix reduces parametrization to Type and Size
template <typename T, int Size>
using Square = Matrix<T, Size, Size>; 

// (2) A Vector restricts the column count to 1
template <typename T, int Line>
using Vector = Matrix<T, Line, 1>; 

This drastically improves readability. Using Square<int, 4> is much safer and clearer than manually typing Matrix<int, 4, 4> every time.

The C++14 Type-Traits Helpers

Alias templates revolutionized the <type_traits> library. Pre-C++14, removing a reference required verbose typename and ::type extraction:

static_cast<typename std::remove_reference<T>::type&&>(arg); // C++11

C++14 introduced the _t suffix using alias templates, heavily reducing visual clutter:

template< class T >
using remove_reference_t = typename std::remove_reference<T>::type;

static_cast<std::remove_reference_t<T>&&>(arg); // C++14: Clean and concise

2. The Three Flavors of Template Parameters

Templates are not restricted to just generic “Types”. They can accept three distinct categories of parameters, allowing for robust compile-time polymorphism.

Type Parameters

The most ubiquitous template parameter, representing a type (e.g., int, std::string, or custom classes).

std::vector<int> myVec;
std::map<std::string, double> myMap;
std::lock_guard<std::mutex> myLockGuard;

Non-Type Parameters (Compile-Time Values)

Non-types are values evaluated at compile-time. They enforce memory bounds and configuration at compile time, eliminating runtime overhead. Supported non-types include:

  • Integral values (e.g., sizes, dimensions)
  • Pointers and Lvalue references
  • nullptr
  • Enumerators
  • Floating-point values (Since C++20)

A classic example is std::array, where the size is hardcoded into the type signature itself for optimal performance and bounds-safety:

std::array<int, 5> myArray{1, 2, 3, 4, 5}; // '5' is a non-type parameter

Template Template Parameters

Sometimes, you need to pass a template as a parameter to another template. For example, if you want a custom Matrix to use an underlying standard container (like std::vector), but you want the user to specify which container template to use:

// (1) Pre-C++17 Syntax (using 'class')
template <typename T, template <typename, typename> class Cont>
class Matrix { /* ... */ };

Notice the nested template <typename, typename> class Cont. It specifies that Cont must be a template that takes two parameters (e.g., the element type and its allocator, which fits std::vector).

Modernizing with C++17 Before C++17, you were forced to use the class keyword for template template parameters. Since C++17, you can use the more semantically accurate typename keyword:
// (2) C++17 Syntax (using 'typename')
template <typename T, template <typename, typename> typename Cont>
class Matrix { /* ... */ };

3. Architectural Perspective: Why This Matters

  • Zero-Cost Abstractions: Non-type parameters (like array sizes or dimensional constraints) are baked directly into the binary layout. The compiler can aggressively optimize these values, leading to higher execution speeds.
  • Self-Documenting Code: Alias templates map dense, multi-parameter generic code into domain-specific terms (e.g., Square instead of Matrix<T, N, N>).
  • Compile-Time Safety: By demanding template-template parameters or non-type parameters, you explicitly constrain what developers can pass into your libraries. If a developer tries to pass a dynamically-sized std::forward_list into a template requiring a compile-time size, the compilation simply fails—preventing subtle runtime crashes.

This site uses Just the Docs, a documentation theme for Jekyll.