C++: Introduction to Template Metaprogramming

Last updated: 2026-08-26

In lesson 46 we learned about advanced smart pointers.

Now, we'll touch upon C++'s "dark magic" — template metaprogramming.

Template metaprogramming is a technique that executes code at compile time, which can significantly improve runtime performance.


1. Template Metaprogramming Overview

(1) 1.1 What Is Template Metaprogramming?

Template Metaprogramming (TMP) is a technique that uses templates to perform computations at compile time.

Characteristics:


(2) 1.2 Why Use Template Metaprogramming?

Advantage Description
Performance Compile-time computation, faster at runtime
Type safety Compile-time type checking
Generics Write truly generic code


2. Compile-Time Computation

(1) 2.1 Compile-Time Factorial

Example: Computing factorial with templates (Difficulty ⭐⭐⭐)

▶ Example 1: Code Example (Difficulty ⭐)

TEXT 📖 Display only
#include <iostream>

// Primary template
template<int N>
struct Factorial {
 static const int value = N * FactorialN-1::value;
};

// Specialization: termination condition
template<>
struct Factorial<0> {
 static const int value = 1;
};

int main() {
 std::cout << "5! = " << Factorial<5>::value << std::endl; // 120
 // Result computed at compile time, used directly at runtime
 
 return 0;
}

Output:

TEXT 📖 Display only
5! = 

▶ Example 2: Code Example (Difficulty ⭐)

💡 Tip:


C++11 introduced constexpr, making compile-time computation simpler.

Example: Computing factorial with constexpr (Difficulty ⭐⭐)

CPP
#include <iostream>

constexpr int factorial(int n) {
 return n <= 1 ? 1 : n * factorial(n - 1);
}

int main() {
 constexpr int result = factorial(5); // Compile-time computation
 std::cout << "5! = " << result << std::endl; // 120
 
 return 0;
}

Output:

TEXT 📖 Display only
5! = 

💡 Tip:



3. Type Traits

(1) 3.1 What Are Type Traits?

Type Traits are techniques for querying or modifying type information at compile time.

Example: Using type_traits to check types (Difficulty ⭐⭐)

TEXT 📖 Display only
#include <iostream>
#include <type_traits>

int main() {
 std::cout << std::is_integralint::value << std::endl; // 1 (true)
 std::cout << std::is_integraldouble::value << std::endl; // 0 (false)
 std::cout << std::is_pointer<int*>::value << std::endl; // 1 (true)
 
 return 0;
}

(2) 3.2 Custom Type Traits

Example: Detecting whether a class has a member function (Difficulty ⭐⭐⭐⭐)

CPP
#include <iostream>
#include <type_traits>

// Detect whether there is a serialize member function
template<typename T>
struct has_serialize {
private:
 template<typename U>
 static auto test(int) -> decltype(std::declvalU().serialize(), std::true_type{});
 
 template<typename U>
 static std::false_type test(...);
 
public:
 static const bool value = decltype(testT(0))::value;
};

struct Person {
 void serialize() {}
};

int main() {
 std::cout << has_serializePerson::value << std::endl; // 1 (true)
 std::cout << has_serializeint::value << std::endl; // 0 (false)
 
 return 0;
}


4. SFINAE

(1) 4.1 What Is SFINAE?

SFINAE (Substitution Failure Is Not An Error): When template argument substitution fails, it is not an error — the compiler will try other overloads.

Purpose: Select different function overloads based on type characteristics.


(2) 4.2 Example: SFINAE for Selecting Overloads (Difficulty ⭐⭐⭐⭐)

TEXT 📖 Display only
#include <iostream>
#include <type_traits>
#include <string>

// Version 1: for integer types
template<typename T>
typename std::enable_if<std::is_integralT::value, std::string>::type
toString(T value) {
 return std::to_string(value) + " (integer)";
}

// Version 2: for other types
template<typename T>
typename std::enable_if<!std::is_integralT::value, std::string>::type
toString(T value) {
 return "Not an integer";
}

int main() {
 std::cout << toString(42) << std::endl; // 42 (integer)
 std::cout << toString(3.14) << std::endl; // Not an integer
 
 return 0;
}

Output:

TEXT 📖 Display only
(program output)


5. Variadic Templates

(1) 5.1 What Are Variadic Templates?

Variadic Templates allow templates to accept an arbitrary number of arguments.

Example: Printing any number of arguments (Difficulty ⭐⭐⭐)

CPP
#include <iostream>

// Recursion termination function
void print() {
 std::cout << std::endl;
}

// Recursive print
template<typename T, typename... Args>
void print(T first, Args... rest) {
 std::cout << first << " ";
 print(rest...); // Recursive call
}

int main() {
 print(1, 2.5, "hello", 'a');
 // Output: 1 2.5 hello a
 
 return 0;
}


6. C++17 Fold Expressions

(1) 6.1 Basic Usage

C++17 introduced fold expressions, which simplify variadic templates.

Example: Summation (Difficulty ⭐⭐)

CPP
#include <iostream>

template<typename... Args>
auto sum(Args... args) {
 return (args + ...); // Fold expression
}

int main() {
 std::cout << sum(1, 2, 3, 4, 5) << std::endl; // 15
 return 0;
}

▶ Example 3: constexpr Compile-Time Computation (Difficulty ⭐)

CPP
#include <iostream>

// constexpr functions are evaluated at compile time
constexpr int square(int n) {
    return n * n;
}

constexpr int cube(int n) {
    return n * n * n;
}

int main() {
    // Compile-time computation, results embedded directly
    constexpr int sq = square(5);
    constexpr int cb = cube(3);

    std::cout << "5^2 = " << sq << std::endl;
    std::cout << "3^3 = " << cb << std::endl;

    // Static assertions verify compile-time evaluation
    static_assert(sq == 25, "square(5) should be 25");
    static_assert(cb == 27, "cube(3) should be 27");

    return 0;
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
5^2 = 
3^3 = 

❓ FAQ

Q: Is template metaprogramming hard to learn? A: Yes. It's recommended to master basic templates first before learning metaprogramming. Most projects don't need metaprogramming.


Q When should I use template metaprogramming?
A - Writing libraries (like the STL) - When you need extreme performance - When you need compile-time type checking

Q Can constexpr replace template metaprogramming?
A Partially. C++14/17 expanded constexpr, and many compile-time computations can now be implemented with constexpr functions.

📖 Summary

Topic Key Points
Compile-time computation Template recursion or constexpr
Type traits type_traits library
SFINAE Select overloads based on type
Variadic templates Accept arbitrary number of arguments
Fold expressions C++17, simplifies variadic templates

📝 Exercises

  1. Basic (Difficulty ⭐): Write a constexpr function to compute factorial, evaluated at compile time. Verify the result using static_assert.

  2. Intermediate (Difficulty ⭐⭐): Use std::enable_if to implement a function template that is only enabled when T is an integer type. Floating-point types should cause a compilation error.

  3. Challenge (Difficulty ⭐⭐⭐): Use variadic templates to implement a print_all function that accepts any number and type of arguments and prints them one by one. Hint: Use recursive expansion or fold expressions (C++17).



Next lesson: C++17/20 New Features (#48)

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏