DEV Community

Cover image for Mastering C++ Templates: Why Generic Programming Beats Code Duplication
Sk
Sk

Posted on

Mastering C++ Templates: Why Generic Programming Beats Code Duplication

To master generic programming, you first need to understand the problem it’s solving. Once that clicks, everything else falls into place.


The Core Problem

In low-level languages, once you assign a block of memory a type (e.g., int32), it’s locked in.

square typeOfSquareVar = square{};
Enter fullscreen mode Exit fullscreen mode

That variable can only ever hold a square type. You can’t shove a rectangle in there, but that’s a good thing, it keeps your program safe and fast.

So here’s the question: what if you write a function that works on square, but could just as easily work on rectangle?

void getShapeColor(square s) {
  // return color of shape in a graphics library
}
Enter fullscreen mode Exit fullscreen mode

rectangles (and circles, triangles, whatever) also have colors. Do we duplicate this function for every shape? That’s messy.

This is exactly the problem generic programming solves: instead of writing one version per type, you write a recipe once, and the compiler generates the correct type-specific version for you(examples below will make this clear).


The Sum Example

Low-level languages have tons of number types:

  • uint8_t through uint64_t
  • float, double
  • signed vs unsigned
uint8_t num1 = 26;  // 0 to 255
uint8_t num2 = 254;
Enter fullscreen mode Exit fullscreen mode

A basic sum function might look like:

uint8_t sum(uint8_t a, uint8_t b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Great, until you need uint16_t:

uint16_t num3 = 300;
uint16_t num4 = 301;

uint16_t sum(uint16_t a, uint16_t b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

And what about floats?

float num5 = 1.0f;
float num6 = 2.0f;

float sum(float a, float b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Do you see the problem? All these functions do the same thing, add numbers, but we’re duplicating code because of static typing.


Enter Generic Programming

Templates let you write the logic once. The compiler customizes it at compile time for whatever type you use.

template <typename T>  // T is a placeholder for any type
T sum(T a, T b) {
    return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Now you can call:

sum(2, 2);     // int
sum(2.0, 2.0); // float
Enter fullscreen mode Exit fullscreen mode

The compiler generates the correct type-safe versions for you.


The Stack Example

A more practical case: building a stack.

#include <iostream>
#include <vector>

template <typename T>
class SimpleStack {
private:
    std::vector<T> items;  // Dynamic array that holds any type 
public:
    void push(T item) { items.push_back(item); }
    T pop() {
        if (items.empty()) throw std::out_of_range("Stack empty!");
        T top = items.back();
        items.pop_back();
        return top;
    }
};
Enter fullscreen mode Exit fullscreen mode

Instead of hardcoding the stack to one type, we make it generic with T.

Usage:

int main() {
    SimpleStack<int> intStack;   // stack of ints
    intStack.push(10);
    intStack.push(20);
    std::cout << intStack.pop() << std::endl;  // Outputs 20

    SimpleStack<std::string> stringStack;  // stack of strings
    stringStack.push("Hello");
    stringStack.push("World");
    std::cout << stringStack.pop() << std::endl;  // Outputs "World"

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Without templates, you’d have to duplicate this class for every type (or resort to ugly type hacks). With templates, the compiler automatically generates SimpleStack<int>, SimpleStack<std::string>, etc.

This is exactly how the Standard Template Library (STL) works. Classes like std::vector<T> and std::map<K, V> are all templates.


Why Generic Programming Matters

Generic programming gives you:

  • Reusability - One function/class works with many types.
  • Type safety - Compiler checks everything upfront.
  • Performance - Zero-cost abstraction - No runtime overhead; templates are resolved at compile time.

In Zig, they call this comptime. In C++, it's templates. Different names, same powerful idea: write once, let the compiler specialize everywhere.

Top comments (0)