Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A C++ function template is a compiler-level blueprint that defines a family of functions capable of operating on generic types. Instead of writing multiple overloaded functions for different data types, a template allows the compiler to automatically generate type-specific function definitions (instantiations) at compile time based on the arguments provided.

Syntax

A function template is declared using the template keyword followed by a template parameter list enclosed in angle brackets < >.
template <typename T>
T function_name(T parameter1, T parameter2) {
    // Function body
    return parameter1;
}
  • template: Instructs the compiler that the following declaration is a template.
  • <typename T>: The template parameter list. T is a placeholder for a data type. The keyword class can be used interchangeably with typename in this context (e.g., template <class T>).
  • Multiple Parameters: Templates can accept multiple type parameters, separated by commas (e.g., template <typename T, typename U>).

Template Instantiation

Templates are not actual functions; they are patterns. When a template is used, the compiler performs instantiation (specifically, monomorphization), generating a concrete function for the specific type. Implicit Instantiation: The compiler automatically deduces the type from the function arguments and generates the corresponding function.
template <typename T>
void process(T val) { }

// Compiler deduces T as int, generates: void process(int val)
process(42); 

// Compiler deduces T as double, generates: void process(double val)
process(3.14); 
Explicit Instantiation: You can force the compiler to generate a function definition for a specific type without calling it, which is often used to manage compilation times and linkage.
// Forces the instantiation of the template for the 'float' type
template void process<float>(float val);

Template Argument Deduction and Type Conversions

When a function template is called, the compiler attempts to deduce the template arguments (T) from the types of the function arguments. While the compiler will not perform implicit type conversions to deduce a template type, it will perform standard type conversions for parameters whose types are explicitly specified or have already been deduced from other arguments.
template <typename T>
void evaluate(T a, int b) { }

// T is deduced as double from '5.5'. 
// The second argument '5.5' undergoes a standard implicit conversion to 'int'.
evaluate(5.5, 5.5); 

template <typename T>
void compare(T a, T b) { }

// compare(5, 5.5);  // Error: Ambiguous deduction (int vs double)

// Explicitly specifying the template argument resolves ambiguity.
// T is explicitly double, so the integer '5' is implicitly converted to double.
compare<double>(5, 5.5); 

Non-Type Template Parameters

Function templates can also accept non-type parameters, which are constant values evaluated at compile time. Historically, these were restricted to integral types, enumerations, pointers, or references. As of C++20, floating-point types and literal class types are also permitted as non-type template parameters.
template <typename T, int N>
void applyMultiplier(T val) {
    T result = val * N;
}

// N is explicitly provided as 10 at compile time
applyMultiplier<double, 10>(5.5); 

Explicit Specialization

If the generic template implementation is not suitable for a specific data type, you can provide an explicit specialization (full specialization).
// Base template
template <typename T>
void execute(T data) {
    // Generic implementation
}

// Explicit specialization for char*
template <>
void execute<char*>(char* data) {
    // Implementation specific to char pointers
}

Overload Resolution and Partial Ordering

Function templates can be overloaded with non-template functions or other function templates. In C++, overload resolution is a unified process. The compiler evaluates all viable candidates (both non-template functions and base templates) simultaneously and ranks them based on the cost of their implicit conversion sequences. Explicit specializations do not participate in overload resolution; they are only considered after a base template has been selected. The resolution process follows these rules:
  1. Simultaneous Evaluation: The compiler gathers all non-template functions and base templates matching the name and argument count. It performs template argument deduction for the templates to create viable function signatures.
  2. Ranking by Conversion Sequences: All viable candidates are ranked based on how well the arguments match the parameters (Exact Match > Promotion > Standard Conversion).
  3. Tie-Breakers: If multiple candidates are equally ranked, the compiler applies tie-breaking rules:
    • Non-Template Preference: If a non-template function and a template instantiation are equally good matches, the non-template function is preferred.
    • Partial Ordering of Templates: If multiple base templates are equally good matches, the compiler applies partial ordering rules to select the more specialized template. A template is considered more specialized if it accepts a narrower set of types.
  4. Specialization Check: Once the best-matching base template is selected, the compiler checks if an explicit specialization exists for the deduced types. If so, it uses the specialization; otherwise, it instantiates the base template.
Partial Ordering Example:
template <typename T>
void print(T data) {
    // Base template 1 (Less specialized)
}

template <typename T>
void print(T* data) {
    // Base template 2 (More specialized for pointers)
}

int val = 42;
print(val);   // Calls Base template 1 (T deduced as int)
print(&val);  // Calls Base template 2 (T deduced as int, T* is a better/more specialized match than T)
Master C++ with Deep Grasping Methodology!Learn More