Advanced Templates

Back

Loading concept...

🚀 Advanced C++ Templates: The Magic Toolbox

Imagine you have a magic toolbox. Instead of having separate tools for each job, you have ONE tool that transforms into whatever you need. That’s what Advanced Templates do in C++!


🎯 What You’ll Master

  • Variadic Templates – Accept any number of ingredients
  • Fold Expressions – Combine ingredients automatically
  • SFINAE – Smart tool selection
  • Concepts – Clear instruction labels
  • Requires Clauses – Safety checks before use

🍳 The Universal Analogy: The Magic Kitchen

Think of C++ templates as a magic kitchen:

  • Your recipe (template) works with ANY ingredients
  • You can add 1, 5, or 100 ingredients (variadic)
  • The kitchen auto-combines them (fold expressions)
  • It refuses wrong ingredients quietly (SFINAE)
  • It has clear labels on what goes where (concepts)
  • It checks ingredients before cooking (requires)

1ïžâƒŁ Variadic Templates

The Story

Imagine you’re making a smoothie. Sometimes you want 2 fruits. Sometimes 10. You don’t want 10 different blenders!

Variadic templates = ONE blender that accepts ANY number of fruits.

The Magic Words

template<typename... Fruits>
void blend(Fruits... fruits);

The ... is the magic! It means “any number of things.”

Simple Example

// Print anything, any amount!
template<typename... Args>
void printAll(Args... args) {
    // We'll combine these next!
}

// Use it:
printAll(1, "hello", 3.14);
printAll("just one");
printAll(1, 2, 3, 4, 5);

What’s Happening?

graph TD A["printAll called"] --> B{How many args?} B --> C["1 arg? OK!"] B --> D["3 args? OK!"] B --> E["100 args? OK!"] C --> F["Template expands"] D --> F E --> F

Key Terms

Term Meaning
typename... “Zero or more types”
Args... A parameter pack
args... Expanding the pack

2ïžâƒŁ Fold Expressions

The Story

You have 5 numbers. You want to add them ALL. Before C++17, this was hard. Now? One line.

Fold expressions = The kitchen automatically mixes all ingredients.

The Four Fold Types

// LEFT fold: ((1 + 2) + 3) + 4
(... + args)

// RIGHT fold: 1 + (2 + (3 + 4))
(args + ...)

// LEFT fold with start: ((0 + 1) + 2) + 3
(init + ... + args)

// RIGHT fold with start: 1 + (2 + (3 + 0))
(args + ... + init)

Simple Example

template<typename... Numbers>
auto addAll(Numbers... nums) {
    return (... + nums);  // Magic!
}

// Use it:
int sum = addAll(1, 2, 3, 4, 5);
// Result: 15

Visual: How Folding Works

graph TD A["&#35;40;... + nums&#35;41;"] --> B["nums = 1, 2, 3"] B --> C["Step 1: 1 + 2 = 3"] C --> D["Step 2: 3 + 3 = 6"] D --> E["Result: 6"]

More Fold Magic

// Print all with commas
template<typename... Args>
void printComma(Args... args) {
    ((std::cout << args << ", "), ...);
}

// Check if ALL are true
template<typename... Bools>
bool allTrue(Bools... b) {
    return (... && b);
}

3ïžâƒŁ SFINAE

The Story

SFINAE = “Substitution Failure Is Not An Error”

Imagine a restaurant with two doors:

  • Door A: “Customers with reservations”
  • Door B: “Walk-ins welcome”

If you don’t have a reservation, Door A doesn’t yell at you. It just
 doesn’t open. You try Door B instead!

How It Works

// This only works for numbers
template<typename T>
typename std::enable_if<
    std::is_arithmetic<T>::value, T
>::type
double_it(T x) {
    return x * 2;
}

// This works for strings
template<typename T>
typename std::enable_if<
    !std::is_arithmetic<T>::value, T
>::type
double_it(T x) {
    return x + x;  // Concatenate!
}

The Magic Flow

graph TD A["Call double_it"] --> B{Is it a number?} B -->|Yes| C["First version"] B -->|No| D{Is it a string?} D -->|Yes| E["Second version"] D -->|No| F["Compile error"] C --> G["x * 2"] E --> H["x + x"]

Simpler SFINAE with void_t

// Check if T has .size()
template<typename T, typename = void>
struct has_size : std::false_type {};

template<typename T>
struct has_size<T,
    std::void_t<decltype(
        std::declval<T>().size()
    )>
> : std::true_type {};

Why “Not An Error”?

Situation Without SFINAE With SFINAE
Wrong type tried đŸ’„ Error! đŸ€· Try next
No match found đŸ’„ Error! đŸ’„ Error!
Match found ✅ Works ✅ Works

4ïžâƒŁ Concepts (C++20)

The Story

SFINAE works but it’s ugly. Like writing “NO DOGS ALLOWED” in tiny print on page 47.

Concepts = Big, clear signs at the entrance! đŸȘ§

Defining a Concept

// "Addable" means: can use + operator
template<typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::same_as<T>;
};

Using Concepts

// Method 1: After template
template<typename T>
    requires Addable<T>
T add(T a, T b) {
    return a + b;
}

// Method 2: In template
template<Addable T>
T add(T a, T b) {
    return a + b;
}

// Method 3: Shorthand
Addable auto add(Addable auto a,
                 Addable auto b) {
    return a + b;
}

Built-in Concepts

#include <concepts>

std::integral<T>      // int, long, etc.
std::floating_point<T> // float, double
std::same_as<T, U>    // T is exactly U
std::convertible_to<From, To>
std::derived_from<Child, Parent>

SFINAE vs Concepts

graph LR A["Old Way: SFINAE"] --> B["enable_if&lt;is_integral&lt;T&gt;&gt;"] C["New Way: Concepts"] --> D["requires std::integral&lt;T&gt;"] B --> E["Hard to read đŸ˜”"] D --> F["Easy to read 😊"]

5ïžâƒŁ Requires Clauses

The Story

Concepts say WHAT something must be. Requires clauses say exactly WHAT IT MUST DO.

Like a checklist:

  • ✅ Can it be copied?
  • ✅ Can it be printed?
  • ✅ Does it have .size()?

Basic Syntax

template<typename T>
    requires std::is_integral_v<T>
void onlyIntegers(T x) { }

Compound Requirements

template<typename T>
    requires std::integral<T> &&
             (sizeof(T) >= 4)
void bigIntegers(T x) { }

The requires Expression

template<typename T>
concept Printable = requires(T x) {
    // Simple: this must compile
    std::cout << x;

    // Compound: must return bool
    { x.empty() } -> std::same_as<bool>;

    // Nested: must satisfy concept
    requires std::movable<T>;
};

Requires-Requires (Yes, Twice!)

template<typename T>
    requires requires(T x) {
        x.push_back(1);
    }
void needsPushBack(T& container) {
    container.push_back(42);
}

First requires = “I have a requirement” Second requires = “Here’s what I’m checking”


🎼 Putting It All Together

Complete Example

#include <concepts>
#include <iostream>

// Concept: must be printable
template<typename T>
concept Printable = requires(T x) {
    std::cout << x;
};

// Variadic + Concepts + Fold
template<Printable... Args>
void printAll(Args... args) {
    ((std::cout << args << " "), ...);
    std::cout << "\n";
}

int main() {
    printAll(1, 2.5, "hello");
    // Output: 1 2.5 hello
}

The Complete Flow

graph TD A["Write Template"] --> B["Add Concept"] B --> C["Use Variadic Pack"] C --> D["Fold to Combine"] D --> E["Clean, Safe Code!"]

🧠 Quick Reference

Feature Purpose Example
typename... Any number of types template<typename... T>
(... + x) Combine with operator return (... + nums);
enable_if Old-style constraints SFINAE
concept Named constraint concept Addable = ...
requires Inline constraint requires std::integral<T>

💡 Remember

  1. Variadic = Accept anything, any amount
  2. Fold = Combine automatically
  3. SFINAE = Silent failure, try next
  4. Concepts = Clear, readable rules
  5. Requires = Precise checks

🚀 You Did It!

You now understand the most powerful features of C++ templates!

These aren’t just “advanced” – they’re the tools that make modern C++ libraries possible. Every time you use std::vector, std::optional, or any modern library, these features are working behind the scenes.

Go forth and template everything! 🎉

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.