CppFunction Templates

Function Templates

A function template defines a family of functions that share the same logic but can operate on different types. You declare it with the template keyword followed by one or more type parameters, and then write the function body exactly as you would for a concrete type.
Basic syntax

basic_template.cpp

CPP
#include <iostream>

template <typename T>
T maxValue(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    std::cout << maxValue(10, 20) << std::endl;   // T = int
    std::cout << maxValue(3.5, 1.2) << std::endl; // T = double
    return 0;
}
typename introduces the placeholder T (you can use the older keyword class instead of typename here — they are interchangeable in this position — but typename is the more common modern style). T is just a name; you could call it anything, but single capital letters like T, U, and V are the conventional choice.
Template argument deduction
In almost all cases, you never need to tell the compiler what T should be — it looks at the arguments you pass and figures it out automatically. This is called template argument deduction. When you call maxValue(10, 20), the compiler sees two int arguments and deduces T = int.
Explicit instantiation

Occasionally you need to be explicit — for example, when the argument types don't match and deduction would be ambiguous, or when you want to force a particular type. You can supply the template argument directly with angle brackets:

explicit_instantiation.cpp

CPP
#include <iostream>

template <typename T>
T maxValue(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    // Without <int>, this would fail: deduction can't decide
    // between T = int and T = double from mismatched arguments.
    std::cout << maxValue<int>(3, 5.9) << std::endl; // forces T = int, prints 5

    // Explicitly requesting double even though both args are int literals.
    std::cout << maxValue<double>(3, 5) << std::endl;

    return 0;
}
Multiple template parameters

A template can take more than one type parameter, which is useful when the function needs to combine or relate two different types.

multiple_params.cpp

CPP
#include <iostream>

template <typename T, typename U>
void printPair(T first, U second) {
    std::cout << "(" << first << ", " << second << ")" << std::endl;
}

int main() {
    printPair(1, "one");      // T = int, U = const char*
    printPair(3.14, 'a');     // T = double, U = char
    return 0;
}
Template definitions usually belong in headers
Templates aren't ordinary functions — the compiler needs to see the full definition of a template at the point where it's used, because it has to instantiate a real function for each type on the spot. If you split a template's declaration into a header and its definition into a separate .cpp file (the usual pattern for non-template code), other translation units that only see the header won't be able to instantiate it, and you'll get linker errors. This is why template code is almost always written entirely in header files (or in a file included at the bottom of a header).
Deduction has limits
Deduction only works from function arguments, and it does not perform implicit conversions to find a common type. Two arguments of different types passed to a single-parameter template like maxValue(T a, T b) will fail to compile unless you either convert one argument yourself or specify the template argument explicitly, as shown above.
What you can put in a template body
  • Any operation the compiler can validate once T is known — arithmetic, comparisons, member access, and so on.

  • A template only fails to compile for a given type if that type doesn't support an operation actually used inside it (this is related to a principle called SFINAE, and later refined by C++20 concepts).

  • You can mix template and non-template parameters in the same function.