Function Templates are blueprints from which the compiler creates functions.
The basics
When you call a template function in your code, the compiler generates a real function to match the arguments passed in to it. You can see this by using cppinsights.io to examine your code. Template functions work as follows:
#include <iostream>
template <typename T> T maximum(T a, T b);
int main() {
std::cout << maximum(9, 5) << std::endl;
std::cout << maximum(6.5, 5.9) << std::endl;
std::string s1 {"Hello"};
std::string s2 {"World"};
std::cout << maximum(s1, s2) << std::endl;
return 0;
}
template <typename T> T maximum(T a, T b) {
return (a > b) ? a : b;
}
in this example, both arguments to the template function have to be of the same type. You can pass arguments of differing types by changing the template definition as follows:
template <typename R, typename T, typename U> R maximum(T a, U b);
The compiler can’t deduce the return type, we need to give it a hand by explicitly passing the type when calling the function. This function is called by specifying the type when you call the function:
int a {5};
int b {7};
double c = maximum<double>(a, b);
You can pass one, two, or all of the types with the call. However, it is better to use auto for the return type:
template <typename T, typename U> auto maximum(Ta, U b);
Just remember that when using auto for a return type, the function definition must be before the function call.
The types of the arguments must be supported by the actions the template function performs. For example, you can’t pass strings to a function that multiplies its arguments without getting a compiler error. Calling our example function with pointer arguments will compile but will produce unexpected results because it will compare the addresses of the variables instead of their content.
Function templates can also be called by reference by adding an & in the appropriate places:
template <typename T> T& maximum(T& a, T& b);
Not all parameters have to be template parameters:
template <typename T> T& maximum(T& a, int b);
Template Specialization
You can explicitly declare a template to override the function that gets called. For example, going back to our pointer example, you could explicitly declare a template for char * as follows:
template <> const char * maximum<const char *>(const char *a, const char *b);
Auto as function templates
Instead of coding a function template, you can code a function using auto. In this case, the compiler will generate a template function under the hood. The syntax is as follows:
auto maximum(auto a, auto b) {
return (a > b) ? a : b;
}
Even though it’s less to type, I don’t like this because it’s harder to reason about.
Non-type template parameters
You can have non-type template parameters to template functions which just means you can pass in a value as a parameter.
<template int b, typename T> T maximum(T a);
Again, I don’t like this because it makes you code harder to reason about than just using a normal parameter,