Introduction to Function Templates
A function template parameterizes a function with types, so one definition can serve many element types.
The Repetition Problem Behind Templates
Imagine you need a function that adds up the values stored in a collection. Today the collection holds integers. Tomorrow it holds decimal numbers, and the day after it holds some other kind of element. If every one of these cases needed its own separately written function, most of the logic would be copied again and again, changing only the type named in the code.
A function template is a way to avoid that repetition. You write the function once and leave the type open until the function is actually used. The rest of this article explains what it means to leave a type open, how the compiler fills it in, and why this idea matters so much in the standard library.
Predicting the Output of a Generic Sum
Before reading the explanation, look at the program below. It contains one function that sums the values of any container of numbers. Try to decide what it prints, and notice how the same function body handles two different element types.
What do you think happens?
What does the program print on each of its two lines?
Reveal answer
12 and 3.75
The first call adds 3, 4, and 5 to get 12. The second call adds 1.5 and 2.25 to get 3.75. The function body is identical in both calls; only the element type differs.
Parameterizing a Function With a Type
A function template parameterizes a function with types, so one definition can serve many element types. The template is a pattern. It becomes a concrete function once a type is supplied at the point of use.
The source's central example is a function that calculates the sum of the element values of any container. Read that function as a plan rather than as a single function. The job is always the same: walk through the elements and add their values together. What changes from one container to the next is only the kind of element being added. A function template captures the fixed job and leaves the element type open.
In the program above, the line template <typename T> introduces T as a type parameter. T is a placeholder name. Everywhere the body says T, the compiler will eventually substitute a real type such as int or double. The words typename T mean that T stands for some type, not for a value.
Tracing Two Calls Through One Template
Following sum_values(counts) from call to result
The call sum_values(counts) is made, where counts is a std::vector<int> holding 3, 4, and 5. Trace how the compiler decides which version of the template to use and what the function computes.
- 1Read the argument type
The argument counts has type std::vector<int>. The parameter of the template is const std::vector<T>&, so the compiler matches the pattern and sees that T must be int.
- 2Fill in the type parameter
With T equal to int, the compiler produces a concrete function
sum_values<int>. Every occurrence of T in the body now means int. - 3Initialize the total
The line T total = T(); becomes int total =
int(); which starts total at 0. - 4Accumulate the elements
The loop visits 3, then 4, then 5, adding each one to total. After the loop, total holds 12.
- 5Return the result
The function returns 12 as an int, and the program prints 12.
The call prints 12. The second call, sum_values(prices), follows the same steps with T equal to double and prints 3.75.
template <typename T>
T sum_values(const std::vector<T>& items) {
T total = T();
for (const T& value : items) {
total += value;
}
return total;
}The template text contains the placeholder T. No function with that exact text is called at run time.
// Generated for the call sum_values(counts)
int sum_values(const std::vector<int>& items) {
int total = int();
for (const int& value : items) {
total += value;
}
return total;
}T has been replaced by int everywhere. The result is an ordinary function that the compiler can call like any other.
Why Templates Matter in the Standard Library
The source points out that templates have many more uses than simply parameterizing a container with an element type. Two uses are singled out: templates are used extensively to parameterize types, and they are also used to parameterize algorithms. Both appear throughout the standard library.
The same template idea therefore covers two kinds of generality. One is about what kind of element a container holds. The other is about the procedure applied to the data. A container template can hold any element type, and an algorithm template can operate on elements of many types without its source code being rewritten for each one.
The comparison above is a generated illustration. The point it makes is that a generic algorithm is written once against an open type, so the same procedure can be reused across element types. Each type has to support the operations the algorithm relies on.
Mistakes Beginners Make With Templates
Common mistake Treating a template as one ordinary function that works for every type automatically
What it looks likeWriting the template and then assuming the body is valid for a type such as a plain struct without checking the operations used inside it.
Why it failsThe template body is checked against the type it is instantiated with. If that type does not support an operation such as +=, the code using it will not compile.
Do this insteadThink about which operations the template body requires, and make sure each type used with the template supports them.
Common mistake Expecting a template to exist as machine code before any call is made
What it looks likeLooking for a compiled sum_values function in the program when the program never calls it.
Why it failsConcrete functions are produced for the types actually used at call sites. A template with no uses produces no concrete function.
Do this insteadRemember that the template is a pattern, and the concrete function is generated from it once a type is supplied.
Common mistake Confusing a type parameter with a value parameter
What it looks likeWriting template <typename T> and then trying to pass T as a number inside the body.
Why it failsT stands for a type such as int or double. It is not a variable holding a value.
Do this insteadUse T where a type is needed, such as in a declaration, and use ordinary parameters for values.
Common mistake Forgetting that the same body is reused for every type, so a fix made for one type applies to all of them
Writing a Template for Your Own Choice of Type
Write a function template named largest_of that takes two values of the same type T and returns the larger one, using the greater than operator. Then call it once with two int values and once with two double values. Predict the output of each call before you run the code.
Hints
- Start with template <typename T> followed by a function whose parameters and return type use T.
- Both parameters must have the same type T, so the two call arguments must match in type.
- Compare the two values with > and return whichever is larger.
Key Points to Carry Forward
- 1A function template parameterizes a function with types, so one definition can serve many element types.
- 2The template is a pattern that becomes a concrete function once a type is supplied at the point of use.
- 3The same fixed job, such as summing values, can stay in one function while the element type changes.
- 4Templates are used extensively in the standard library to parameterize both types and algorithms.
Key Takeaways
- A function template parameterizes a function with types, so one definition can serve many element types.
- A template is a pattern that becomes a concrete function once a type is supplied at the point of use.
- Templates are used extensively to parameterize both containers' element types and algorithms in the standard library.
- Concrete functions are generated for the types actually used at call sites, and each type must support the operations the template body uses.