SyntaxStudy
Sign Up
C++ Function Templates and Template Argument Deduction
C++ Beginner 1 min read

Function Templates and Template Argument Deduction

Templates are C++'s mechanism for generic programming. A function template parameterises a function over one or more types or values, enabling a single definition to work with many different argument types without sacrificing type safety. The compiler instantiates a concrete function for each unique set of template arguments encountered, generating specialised machine code for each type used. Template argument deduction allows the compiler to infer template parameters from the types of function arguments, so callers rarely need to specify them explicitly. Deduction follows specific rules: 'const' and reference qualifiers are stripped in certain contexts, and arrays decay to pointers. Understanding deduction prevents surprises when templates behave differently than expected. Non-type template parameters accept compile-time constant values such as integers, pointers, or enumerators rather than types. They enable fixed-size arrays and compile-time configuration baked directly into the type system. C++17 introduced 'auto' non-type template parameters, and C++20 expanded the set of valid non-type parameter types considerably.
Example
#include <iostream>
#include <string>
#include <array>

// Function template — T is deduced from arguments
template<typename T>
T maxOf(T a, T b) {
    return (a > b) ? a : b;
}

// Template with two different type parameters
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
    return a + b;
}

// Non-type template parameter: fixed-size stack
template<typename T, std::size_t N>
class FixedStack {
public:
    void push(const T& val) {
        if (top_ >= N) throw std::overflow_error("Stack full");
        data_[top_++] = val;
    }
    T pop() {
        if (top_ == 0) throw std::underflow_error("Stack empty");
        return data_[--top_];
    }
    bool   empty() const { return top_ == 0; }
    std::size_t size()  const { return top_;     }

private:
    std::array<T, N> data_;
    std::size_t      top_ = 0;
};

int main() {
    // Argument deduction — no explicit <int>
    std::cout << maxOf(3, 7)           << "\n";   // 7
    std::cout << maxOf(3.14, 2.71)     << "\n";   // 3.14
    std::cout << maxOf(std::string("apple"), std::string("banana")) << "\n";

    // Mixed types via two-parameter template
    std::cout << add(1, 2.5) << "\n";  // double 3.5

    // Non-type parameter — capacity is part of the type
    FixedStack<int, 4> stack;
    stack.push(10);
    stack.push(20);
    stack.push(30);
    std::cout << "popped: " << stack.pop() << "\n";  // 30

    return 0;
}