19.1 — Function templates

The need for function templates

In previous chapters, you’ve learned how to write functions and classes that help make programs easier to write, safer, and more maintainable. While functions and classes are powerful and flexible tools for effective programming, in certain cases they can also be somewhat limiting because of C++’s requirement that you specify the type of all parameters.

For example, let’s say you wanted to write a function to calculate the maximum of two numbers. You might do so like this:

This function would work great -- for integers. What happens later when you realize your max() function needs to work with doubles? Traditionally, the answer would be to overload the max() function and create a new version that works with doubles:

Note that the code for the implementation of the double version of maximum() is exactly the same as for the int version of max()! In fact, this implementation would work for all sorts of different types: chars, ints, doubles, and if you’ve overloaded the > operator, even classes! However, because C++ requires you to make your variables specific types, you’re stuck writing one function for each type you wish to use.

Having to specify different “flavors” of the same function where the only thing that changes is the type of the parameters can become a maintenance headache and time-waster, and it also violates the general programming guideline that duplicate code should be minimized as much as possible. Wouldn’t it be nice if we could write one version of max() that was able to work with parameters of ANY type?

Welcome to the world of templates.

What is a function template?

If you were to look up the word “template” in the dictionary, you’d find a definition that was similar to the following: “a template is a model that serves as a pattern for creating similar objects”. One type of template that is very easy to understand is that of a stencil. A stencil is an object (e.g. a piece of cardboard) with a shape cut out of it (eg. the letter J). By placing the stencil on top of another object, then spraying paint through the hole, you can very quickly produce stenciled patterns in many different colors! Note that you only need to create a given stencil once -- you can then use it as many times as you like, to create stenciled patterns in whatever color(s) you like. Even better, you don’t have to decide the color of the stenciled pattern you want to create until you decide to actually use the stencil.

In C++, function templates are functions that serve as a pattern for creating other similar functions. The basic idea behind function templates is to create a function without having to specify the exact type(s) of some or all of the variables. Instead, we define the function using placeholder types, called template type parameters. Once we have created a function using these placeholder types, we have effectively created a “function stencil”.

When you call a template function, the compiler “stencils” out a copy of the template, replacing the placeholder types with the actual variable types from the parameters in your function call! Using this methodology, the compiler can create multiple “flavors” of a function from one template! We’ll take a look at this process in more detail in the next lesson.

Creating function templates in C++

At this point, you’re probably wondering how to actually create function templates in C++. It turns out, it’s not all that difficult.

Let’s take a look at the int version of max() again:

Note that there are 3 places where specific types are used: parameters x, y, and the return value all specify that they must be integers. To create a function template, we’re going to replace these specific types with placeholder types. In this case, because we have only one type that needs replacing (int), we only need one template type parameter.

You can name your placeholder types almost anything you want, so long as it’s not a reserved word. However, in C++, it’s customary to name your template types the letter T (short for “Type”).

Here’s our new function with a placeholder type:

This is a good start -- however, it won’t compile because the compiler doesn’t know what “T” is!

In order to make this work, we need to tell the compiler two things: First, that this is a template definition, and second, that T is a placeholder type. We can do both of those things in one line, using what is called a template parameter declaration:

Believe it or not, that’s all we need. This will compile!

Now, let’s take a slightly closer look at the template parameter declaration. We start with the keyword template -- this tells the compiler that what follows is going to be a list of template parameters. We place all of our parameters inside angled brackets (<>). To create a template type parameter, use either the keyword typename or class. There is no difference between the two keywords in this context, so which you use is up to you. Note that if you use the class keyword, the type passed in does not actually have to be a class (it can be a fundamental variable, pointer, or anything else that matches). Then you name your type (usually “T”).

If the template function uses multiple template type parameter, they can be separated by commas:

For classes using more than one type, it’s common to see them named “T1” and “T2”, or other single capital letter names, such as “S”.

One final note: Because the function argument passed in for type T could be a class type, and it’s generally not a good idea to pass classes by value, it would be better to make the parameters and return types of our templated function const references:

Using function templates

Using a function template is extremely straightforward -- you can use it just like any other function. Here’s a full program using our template function:

This will print:


Note that all three of these calls to max() have parameters of different types! Because we’ve called the function with 3 different types, the compiler will use the template definition to create 3 different versions of this function: one with int parameters (named max<int>), one with double parameters (named max<double>), and one with char parameters (named max<char>).

Note that you don’t need to explicitly specify the template type in the function name (e.g. the <int> part of max<int>) so long as the compiler can deduce it from the parameter types.


As you can see, template functions can save a lot of time, because you only need to write one function, and it will work with many different types. Once you get used to writing function templates, you’ll find they actually don’t take any longer to write than functions with actual types. Template functions reduce code maintenance, because duplicate code is reduced significantly. And finally, template functions can be safer, because there is no need to copy functions and change types by hand whenever you need the function to work with a new type!

Template functions do have a few drawbacks, and we would be remiss not to mention them. First, some older compilers do not have very good template support. However, this downside is no longer as much of a problem as it used to be. Second, template functions often produce crazy-looking error messages that are much harder to decipher than those of regular functions (we’ll see an example of this in the next lesson). Third, template functions can increase your compile time and code size, as a single template might be “realized” and recompiled in many files (there are ways to work around this one).

However, these drawbacks are fairly minor compared with the power and flexibility templates bring to your programming toolkit!

Note: The standard library already comes with a templated max() function (in the algorithm header), so you don’t have to write your own (unless you want to). If you do write your own, note the potential for naming conflicts if you use the statement “using namespace std;”, as the compiler will be unable to tell whether you want your version of max() or std::max().

In the rest of this chapter, we’ll continue to explore the topic of templates.

19.2 -- Function template instances
18.x -- Chapter 18 comprehensive quiz

148 comments to 19.1 — Function templates

  • sito

    I feel that all template function calls for the code here should include <int> or <double>(just an example). It improves code readability at least the way i see it and once you start working with allot of code small things like that can help more than you think.

  • J34NP3T3R

    What happens when you mix two different types together ?

    double d = add(3, 7.2);
    std::cout << d << '\n';

    will it still work ? or does it all have to be of one type.
    and what happens when the template encounters an issue with possible loss of data or the likes ?

    • Alex

      Because the template is expecting the same type for each parameter, it will give an error if the parameters are different types. It won't do implicit type conversion.

  • Miguel Cabral

    I think there is a typo (missing the word 'one' after 'which') here: "in this context, so which you use is up to you."

  • yeokaiwei

    1. Query on auto and Templates, T
    Based on the above, T seems to work like an auto type.

    Are there any overlaps between auto and templates?

    2. Is this a bug with Templates?
    This works with Templates

    With char, I'm guessing they compare
    a = 65 (ASCII value)
    6 = 6

    What are the templates with std::string comparing?

    • nascardriver

      1. Yes

      2. You're passing char arrays to `max`, `max` is performing a pointer comparison, the result is undefined. Swap "hello" and "world" and you'll likely get a wrong result. If you passed `std::string`s to `max`, it'd work as expected.

      • yeokaiwei

        1. Thanks for the reply. Got it.

        2. The std::string comparison is very funny. If I add another letter, the whole thing will fail. This is not a major issue to me but I felt that it could be a bug in the future. Someone could use std::string abc = max("hello", "hello"); and it would work, while std::string abc = max("hello", "ahello"); won't work.

        Thanks for the reply.

  • Tim

    >>There is no difference between the two keywords in this context, so which you use is up to you.
    Shouldn't it be 'whichever you use is up to you'.

    I was wondering how we can use template pointers. The following doesn't work

    • nascardriver

      The error message says

      That means `max` has a template parameter named `Y` but the compiler couldn't figure out what `Y` is based on your call to `max`. `max` doesn't use `Y`, so it shouldn't have this template parameter.

  • Tony Kolarek

    Yoo Nascar & Alex, what's up?

    I tried to implement my own Array class with templates, and I wanted to ask if you could provide some valuable feedback on what's good/what's wrong etc. I would appreciate that a lot! :)
    In main() I tested some of the features.

    • nascardriver


      Line 10: You don't know that `value` has type `int`. Deleting this function seems arbitrary, you could instead make the constructor `explicit`.

      Using a parameter pack for the constructor causes hard to understand error messages and doesn't indicate to the user that all types in the pack have to be the same. Using a `std::initializer_list` or turning your class into an aggregate fixes this. If you do use parameter packs and have any type requirements, use concepts (C++20) or type traits (Before C++20) to restrict the input and provide more useful error messages.

      Your destructor doesn't do anything.

      `swap()` should be implemented by means of `std::swap`, but `array.swap(1, 2)` isn't much easier to write than `std::swap(array[1], array[2])` so you could drop this function entirely. I understand `array.swap(1, 2)` is more convenient than letting the user call `std::swap()`, but `std::swap()` has well-known semantics whereas readers of code using your class would have to look up what `swap()` does.

      `getElement()` returns by value.

      Avoid abbreviations, name things descriptively.

      `firstElem < secondElem` implies `firstElem != secondElem`. `firstElem != secondElem` has no effect.

      The `print()` function, oh the `print()` function. `print()` has weird behavior. Together with the misleading name `secondElem`, it's too hard to understand even when reading the implementation. `print()` should have a documentation. This would lead to the next point, a `print()` function should be so simple that it doesn't need a documentation. The solution could be as simple as renaming `firstElem` to "begin" and `secondElem` to "end". The meanings of these names are well established. You should still have documentation for the special -1 values. Documentation? No, back to "a print() function shouldn't need documentation". That means there cannot be parameters, parameters make the function too complicated. Now it doesn't have to be `print()` anymore, it can be `operator<<`. But we don't want to lose the functionality of printing a subarray. A ranged printing function seems like it only has very niche use cases. I can't remember when the last time was I wanted to print a part of an array. What's more useful is extracting a subarray from an array. You can do that by adding an `Array(const Array& other, int begin)` constructor. Now users can create subarrays and if they want to print them, they just pass the new subarray to your `operator<<`.
      But wait, there's more! By having `print()`, you're forcing the user (Or language) to provide `operator<<` for `T`. That's majorly inconvenient, as some types might not even make sense to print. If a user wants to print an `Array`, they can define the operator themselves. You could also use inheritance to add a `PrintableArray` subclass that provide `operator<<` but doesn't prevent `Array` from being used with non-printable types.

      • Tony

        Hey nascar, thanks for your detailed feedback! I have some questions.

        1) "If you do use parameter packs and have any type requirements, use concepts (C++20) or type traits (Before C++20) to restrict the input and provide more useful error messages." --> My idea was to only allow the type used for creating the array to be used in the brackets { }. What do you mean with this line, though?

        2) Extracting the subarray -> Do you have any tips on how I'd do that? I'm asking this because I tried implementing it, but I don't think it's what you meant.

        The first loop copies the subarray into m_array, and the second one iterates until "begin" to set all the values to 0. The only thing that bothers me is that the user must always type the previous myArray's size and type, otherwise this will throw a compiler error... which brings me to the next point.

        The other issue is that if I declare my new array like this:
            container::Array<int, 3> mArray(myArray, 8);
        This will throw a compiler error because myArray's size was 11. The error is "static_cast: cannot convert from 'const container::Array<int, 11> to 'T'"

        Regarding your other points, I fixed the code. Thank you. I added begin() and end() which serve as iterators, so standard library algorithms such as std::sort etc will work. I've kept my swap/print function just for exercise, and changed their parameter names.

        • nascardriver

          I'm only considering the C++20 solutions because they're simpler than trying to do the same in old standards. If you don't want to use C++20 you'll have to look up type traits (Specifically `std::is_same` and fold expressions) and you can't use `std::span`.

          1) Consider the following code

          You didn't restrict `...Args` at all, so according to you template parameters, this is ok. Apart from reading the definition of `Array::Array`, there is no way for the user to know that this constructor call is illegal.
          Of course it causes an error, something along the lines of

          Your constructor is very simple, so the user can probably understand what they did wrong. But they had to read your implementation, which is bad (Not your implementation, the fact that they had to read it). For more complex classes, this is a big issue. The error should instead occur in the code that was written by the user or at least at the constructor's declaration, not in its definition.

          To fix this with concepts, you'd change the template to

          Now, without running into an error, the user can see that every element of the parameter pack has to be a `T`. If the user attempts the above initialization anyway, the error message is

          This is a little longer, but it contains everything that's needed to understand the problem. For one, the error occurred in the user's code, because it was the user's fault. It says that a constraint was not satisfied, and it says that all `T` have to be `int`. It'd be nice if it also said that we gave it a `const char[6]`, but I guess we can't have everything.

          The error message will vary between compilers, but its content should be similar.

          If you use "Array" inside `Array`, it means `Array`, which is why you can't do

          unless `myArray` has the same type as `mArray`. Your constructor needs to have a template non-type parameter to accept an `Array` of any size (But the type has to be the same).

          Don't use `std::move` in this constructor, it doesn't own `other`. You also need to make sure not to overrun `this`' or `other`'s `m_array`, eg. by only looping up to `std::min(size, other_size)`.
          I forgot to add `end` to the subarray constructor in my last comment, so you'd need that too. Try implementing that before you move on to my next idea.

          Next idea: Since you now have `begin()` and `end()`, you can construct a `std::span` from `Array`. `std::span` already has a `subspan()` function which the user could use if you allowed construction of `Array` from `std::span`.

          • Tony Kolarek

            Before I proceed, this is my current solution to the second constructor:

            Could've done it with for loops too apparently, but I find it much more readable this way. What do you think?

            • nascardriver

              You started mixing `int` and `std::size_t` for your indexes. Specify a `size_type` type alias inside the `Array` class and use that everywhere. It not only forces you to use consistent types, but also lets the user of your class use the right type of they want to store an index.
              Line 19: 0 is not a valid value for `T`, `T` can be anything. Use `T()` rather than 0 to get a default-constructed `T` (List initialization would invoke a list constructor if there is one. But we don't want that, so we use direct-initialization).

              You wrote quite a bit of code that isn't very fast to understand. Standard algorithms make it clearer

              • Tony Kolarek

                Thanks for your previous help nascar! I've edited my code to fit the new changes.
                Regarding your last point, I indeed agree. My idea was to try and implement most of the things without the help of the standard library, and that's why I didn't most of these things. That's because I wanted to test myself :)

                This is the class so far:
                (Note that I have kept some functions just because I wanted to practice a bit)

                I'll try to implement your idea asap. But for now, thank you a LOT for your help, you're always the perfect supporter!

  • Tim

    Hello Nascardriver and Alex,

    I tested some codes and encountered something that I couldn't explain:

    Could you tell me why it's a problem that I am returning a reference to a local temporary variable in the add function, but not in the max function?

    I don't see any real difference between the two functions, because both return the evaluation of an expression.

    • Tim

      I thought about it again and think that I know the reason now.

      In the case of add(), I am trying to return a const reference to an expression that resolves to a temporary value, that will go out of scope at the end of the function, therefore I'll end up with a dangling reference.
      The correct way to return the temporary value would be by value.

      In the case of max(), I am returning an const reference to x or y, which were passed by const reference themselves. So that's why it's not a problem to return by const reference in this case.

      Is that correct?

  • salah

    Hi nascardriver,
    so what is the differnce beetween these two codes:

    5 is stored at a temp variable and destroyed at semi-colon; but because it's a const the live of 5 is extended, so no dangling.

    isn't this likes the previous one ? since a temp variable (for max(1, 4)) would be created and destroyed at the end of semi-colon, but because it's a const, it should be extended! so why here we have a dangling ?

    • nascardriver

      Function return values are exempt from lifetime extension, see

  • Asgar

    Hi. For the template example on this page, I almost thought it had a problem due to a dangling reference. That is because max() returns a reference to a temporary and that reference becomes dangling as soon as the execution of the expression containing the function call completes. Then I noticed that the caller code uses non-reference variables i, d, and ch, in order to receive respective outputs from max(). That is safe because a non-reference variable in the call expression safely receives a copy of the temporary by the time the expression completes.

    It would indeed have a problem if i, d, and ch were reference variables, wouldn't it?

    • nascardriver

      This is safe. The temporaries created to call `max` live until the end of the full expression (Usually until the next semicolon) containing the call.

      Only when you try to store the return value, you'll get a problem

      Again the temporaries live until the semicolon. Lifetime extension cannot prevent the temporaries from being destroyed. When we get to printing `i`, it's already dangling.

      To perform such a call, we'd have to use a non-reference

      Now the temporary gets copied into `i` and we don't have to care about the temporaries anymore.

  • Rohit Alawadhi

    in normal function calls this wont bind:

    But this works with templates

    In the background it replaces T with int so should be same as the above implementation. Why does it differ or this is just undefined behaviour running?

  • spaceship_driver

    how do i make a template function to return different types based on the types passed onto the function  ???

    I tried using 'auto' keyword but it didn't work, got lots of errors.

    • nascardriver

      Based on input types would be easy, but you're trying to return different types based on value. Types have to be known at compile-time. Since you don't know if `x` or `y` is less at compile-time, you can't decide on a return type.

      You can use `std::variant`. It can hold any of the types you give it. It doesn't work with references, so you'll have to wrap your references in `std::reference_wrapper`. The caller can then use `std::visit` to call a function with the type that's stored in the variant.

      • spaceship_driver

        sorry but this isn't compiling with my gnu complier .it is showing lots of errors.

        • nascardriver

          `std::variant` requires at least C++17 and g++7.1

          • spaceship_driver

            still it's giving errors.

            Also gcc version is 7.5.0

            • nascardriver

              Thanks for the detailed error report, that's making the problem easier to figure out.

              My code only works since gcc or clang version 9. I wasn't able to figure out if the older versions are missing something or if my code is invalid. In either case, you can fix it by changing line 10 and 14 to

              Also note that the function doesn't work if `T` and `U` are the same type. It'd need further adjustment to be able to cope with that. Try avoiding having to use this function, your code will become more difficult to read. You can probably solve your issue by restructuring your code and using overloads.

  • hellmet

    Oh boi just watched a video on Templates and Concepts... Boi this chapter gonna change a lot with C++20!

  • Nirbhay

    Really enjoyed this lesson! :)

  • Atas

    Isn't max returning dangling references if we feed it rvalues?

    If not, why? I can't see a difference between this and the example in a previous lesson:

    • Passing 3 and 7 to a function that wants references causes 2 temporaries to be created. These temporaries live until the end of the expression that initiated the call. The temporaries live for the entirety of line 9.

      The reference would dangle if you kept it a reference.

      Since you're copying the reference before it starts dangling, there's no problem.

      • Atas

        Ohhh, subtle. I thought the temporaries die when their local scope ends. Thanks!

      • Atas

        A follow-up question, now that I had time to think about it: isn't it dangerous that you can assign the result of a function to a variable, but not to a reference, not even a const reference? The function's prototype doesn't seem to make it immediately obvious that you shouldn't do so. Would declaring

        be better in some contexts?

        • I wouldn't say so.

          If the function's declaration isn't obvious enough, add a comment. Binding references to a value returned by a non-member function is rare and should spark enough concern to check the documentation.

      • hellmet

        Wait I thought 3, 7 in max() were const ref to the original 3, 7, through lifetime extension, as they are const references. Doesn't returning them by reference just pass it back into the caller, at which point, 3, 7 are still in the expression scope and the result can be placed into i (the expression being: int i{max(3, 7)}  )?

        • nascardriver

          This is fine, you're copying the reference to 7 before the 7 dies, so you're storing a copy. The code is only problematic if `i` is a reference.

          • hellmet

            That part is fine, I was confused with the "causes 2 temporaries to be created" part in your comment above.

            • nascardriver

              There's a 3 and a 7. A temporary is created for each of them, which the references `x` and `y` bind to. `i` (Where `i` is a reference) can't extend the lifetime of the temporaries, even though `i` is const, because they were passed to a function and returned by the same function call, after which they will die.

              • hellmet

                Oh! I was under the impression that 3 and 7 themselves have extended lifetimes. I see, so temporaries are created, to which the references bind to! Makes sense, as a reference is more like a fancy address. Thanks for clearing that up!

  • The numbers are divided by the number 3 from 1 to 100 using the Recursion
    الاعداد التي تقبل القسمة على3 من 1 الى100

  • The numbers that are divided into 3 are 1 to 100 using the self-call

  • Hello Alex,
    could i ask your help, i'am creating a database container class that uses a vector of users, where each user has a ID, name, i'am writing my own version of bisection search
    my problem is the bisection search function have to use getname() method when searching for name and use getID() when searching for ID

    so i would need 2 versions of bisection search function thus i tried to make it a template one version function and use overloading casting operator on the user class so when comparing an element of the user vector against the search Element i would just cast it to the type of the parameter the function is being called with static_cast<T1>(Vec[0])

    and it worked but i had to write a code that is ugly and does not make sense:

    - is the binary search function a good candid to be a template version function ?
    - if no, is there a better option than writing 2 function versions ?

    thanks a lot :)

    • Add a third parameter to @binarySearch to pass a comparison function.

      Then use @comp to compare the elements.

      • Hello nascardriver
        i don't know how to use std::function as i don't understand how it works yet, can it perform different operations
        for example my binary search is doing the following comparisons because i'am searching for bounds too (lower bound and upper bound)

        i don't understand can a one function perform multiple comparisons, for example should i have another input describing which comparison to perform ?
        also could you please give me an example of using std::function ...

        • (I forgot a parameter in my previous reply. I added it.)

          In that case, you can change @comp to return an int, indicating the relationship between two objects.

          Now you can use @comp like so

          @std::function is covered in lesson 7.8.

          • Ok thanks i understand it a little more now, i updated the binary function to

            as i did not want to create a user from search element as it would be incomplete (just id is correct or name or age) any one chosen to search and rest of the data would be invalid, but does it make sense to have a compare function like this:

            or should i better create a temp user from the search element and use it for my compare function as it makes more sense ?

            • You don't need a temp user and @User doesn't have to be complete at the declaration or definition of @binarySearch. The caller supplies the comparison function, so only the caller needs the complete type of @User. There will be no  copies of the users, because they're passed by reference.

  • lucieon

    I've seen template keyword being used in place of typename.
    For example:

    Is there any difference between the two?

    • What you've seen is probably "typename", not "template".
      "typename" and "class" are interchangeable in templates.
      If this isn't it, can you provide a link to an example of what you mean? The code you posted is syntactically incorrect.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]