C++ Programming Tutorial

Learn modern C++ step-by-step — from basics to advanced features like templates, STL, and smart pointers. Clear explanations with practical, runnable examples.

C++ Templates

Templates let you write generic code that works with any type. The compiler generates type-specific versions at compile time — giving you the flexibility of generics with zero runtime overhead.

Function Templates

func_templates.cpp
#include <iostream>
#include <string>

// A single function that works with any type
template<typename T>
T max_of(T a, T b) {
    return (a > b) ? a : b;
}

// Template with multiple type parameters
template<typename T, typename U>
auto add(T a, U b) {
    return a + b;  // return type deduced (C++14)
}

int main() {
    std::cout << max_of(3, 7) << "
";           // 7 (int)
    std::cout << max_of(3.14, 2.71) << "
";    // 3.14 (double)
    std::cout << max_of(std::string("abc"), std::string("xyz")) << "
";  // xyz

    std::cout << add(3, 4.5) << "
";   // 7.5 (int + double = double)
    std::cout << add(10, 20) << "
";   // 30

    // Explicit template argument (rarely needed)
    std::cout << max_of<double>(3, 4.5) << "
";

    return 0;
}

Class Templates

class_templates.cpp
#include <iostream>
#include <stdexcept>

// A generic stack that works with any type
template<typename T>
class Stack {
    T data[100];
    int top_index = -1;

public:
    void push(const T& value) {
        if (top_index >= 99) throw std::overflow_error("Stack full");
        data[++top_index] = value;
    }

    T pop() {
        if (top_index < 0) throw std::underflow_error("Stack empty");
        return data[top_index--];
    }

    const T& top() const {
        if (top_index < 0) throw std::underflow_error("Stack empty");
        return data[top_index];
    }

    bool empty() const { return top_index < 0; }
    int size() const { return top_index + 1; }
};

int main() {
    Stack<int> int_stack;
    int_stack.push(10);
    int_stack.push(20);
    std::cout << int_stack.top() << "
";  // 20
    int_stack.pop();
    std::cout << int_stack.top() << "
";  // 10

    Stack<std::string> str_stack;
    str_stack.push("hello");
    str_stack.push("world");
    std::cout << str_stack.top() << "
";  // world

    return 0;
}

Template Specialization

specialization.cpp
#include <iostream>
#include <cstring>

// General template
template<typename T>
bool is_equal(T a, T b) {
    return a == b;
}

// Specialization for C-strings (char*)
template<>
bool is_equal<const char*>(const char* a, const char* b) {
    return std::strcmp(a, b) == 0;
}

int main() {
    std::cout << is_equal(3, 3) << "
";              // 1 (true)
    std::cout << is_equal(3.14, 3.14) << "
";        // 1
    std::cout << is_equal("hello", "hello") << "
";  // 1 (uses specialization)
    std::cout << is_equal("hi", "bye") << "
";       // 0
    return 0;
}

Variadic Templates (C++11)

variadic.cpp
#include <iostream>

// Accept any number of arguments of any types
template<typename T>
void print(const T& value) {
    std::cout << value << "
";  // base case
}

template<typename T, typename... Rest>
void print(const T& first, const Rest&... rest) {
    std::cout << first << " ";
    print(rest...);  // recursive expansion
}

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

Concepts (C++20) — Constraining Templates

concepts.cpp
#include <iostream>
#include <concepts>

// Only allow numeric types
template<typename T>
requires std::integral<T> || std::floating_point<T>
T square(T x) {
    return x * x;
}

// Shorter syntax with concept
template<std::integral T>
T double_val(T x) {
    return x * 2;
}

int main() {
    std::cout << square(5) << "
";    // 25
    std::cout << square(3.14) << "
"; // 9.8596
    // square("hello");  // ERROR: string is not integral or floating_point

    std::cout << double_val(7) << "
"; // 14
    return 0;
}

Best Practices

  • Use templates for type-independent algorithms — avoids code duplication while maintaining type safety.
  • Prefer auto return types (C++14) for template functions to simplify declarations.
  • Use concepts (C++20) to constrain templates — gives clear error messages when wrong types are used.
  • Define templates in headers — the compiler needs the full definition to instantiate them.
  • Avoid over-engineering — only templatize code that genuinely needs to work with multiple types.

Keep Practicing

Use the online compiler to run every example and experiment with modifications. The best way to learn C++ is by writing code — even small programs build strong foundations.