Search

13.2 — Function template instances

It’s worth taking a brief look at how template functions are implemented in C++, because future lessons will build off of some of these concepts. It turns out that C++ does not compile the template function directly. Instead, at compile time, when the compiler encounters a call to a template function, it replicates the template function and replaces the template type parameters with actual types. The function with actual types is called a function template instance.

Let’s take a look at an example of this process. First, we have a templated function:

When compiling your program, the compiler encounters a call to the templated function:

The compiler says, “oh, we want to call max(int, int)”. The compiler replicates the function template and creates the template instance max(int, int):

This is now a “normal function” that can be compiled into machine language.

Now, let’s say later in your code, you called max() again using a different type:

C++ automatically creates a template instance for max(double, double):

and then compiles it.

The compiler is smart enough to know it only needs to create one template instance per set of unique type parameters (per file). It’s also worth noting that if you create a template function but do not call it, no template instances will be created.

Operators, function calls, and function templates

Template functions will work with both built-in types (e.g. char, int, double, etc…) and classes, with one caveat. When the compiler compiles the template instance, it compiles it just like a normal function. In a normal function, any operators or function calls that you use with your types must be defined, or you will get a compiler error. Similarly, any operators or function calls in your template function must be defined for any types the function template is instantiated for. Let’s take a look at this in more detail.

First, we’ll create a simple class:

Now, let’s see what happens when we try to call our templated max() function with the Cents class:

C++ will create a template instance for max() that looks like this:

And then it will try to compile this function. See the problem here? C++ can’t evaluate x > y, because x and y are Cents class objects, and doesn’t know how to compare them. Consequently, this will produce a fairly-tame looking compile error, like this:

1>c:\consoleapplication1\main.cpp(4): error C2676: binary '>': 'const Cents' does not define this operator or a conversion to a type acceptable to the predefined operator
1>  c:\consoleapplication1\main.cpp(23): note: see reference to function template instantiation 'const T &max(const T &,const T &)' being compiled
1>          with
1>          [
1>              T=Cents
1>          ]

The top error message points out the fact that there is no overloaded operator > for the Cents class. The bottom error points out the templated function call that spawned the error, along with the type of the templated parameter.

To get around this problem, simply overload the > operator for any class we wish to use max() with:

Now C++ will know how to compare x > y when x and y are objects of the Cents class! As a result, our max() function will now work with two objects of type Cents.

Another example

Let’s do one more example of a function template. The following function template will calculate the average of a number of objects in an array:

Now let’s see it in action:

This produces the values:

3
5.535

As you can see, it works great for built-in types!

It is worth noting that because our return type is the same templated type as our array elements, doing an integer average will produce an integer result (dropping any fractional value). This is similar to how doing an integer division will produce an integer result. It’s not wrong that we’ve defined things to work that way, but it may be unexpected, so a good comment to users of the class wouldn’t be amiss here.

Now let’s see what happens when we call this function on our Cents class:

The compiler goes berserk and produces a ton of error messages!

example.cpp

(33): error C2679: binary '<<': no operator found which takes a right-hand operand of type 'T' (or there is no acceptable conversion)

        with

        [

            T=Cents

        ]

C:/data/msvc/14.22.27905/include\ostream(437): note: could be 'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_streambuf> *)'
C:/data/msvc/14.22.27905/include\ostream(412): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(const void *)'
C:/data/msvc/14.22.27905/include\ostream(394): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(long double)'
C:/data/msvc/14.22.27905/include\ostream(376): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(double)'
C:/data/msvc/14.22.27905/include\ostream(358): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(float)'
C:/data/msvc/14.22.27905/include\ostream(340): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned __int64)'
C:/data/msvc/14.22.27905/include\ostream(322): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(__int64)'
C:/data/msvc/14.22.27905/include\ostream(304): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned long)'
C:/data/msvc/14.22.27905/include\ostream(286): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(long)'
C:/data/msvc/14.22.27905/include\ostream(268): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned int)'
C:/data/msvc/14.22.27905/include\ostream(248): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(int)'
C:/data/msvc/14.22.27905/include\ostream(230): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned short)'
C:/data/msvc/14.22.27905/include\ostream(202): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(short)'
C:/data/msvc/14.22.27905/include\ostream(184): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(bool)'
C:/data/msvc/14.22.27905/include\ostream(179): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(std::ios_base &(__cdecl *)(std::ios_base &))'
C:/data/msvc/14.22.27905/include\ostream(174): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_ios> &(__cdecl *)(std::basic_ios> &))'
C:/data/msvc/14.22.27905/include\ostream(169): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_ostream> &(__cdecl *)(std::basic_ostream> &))'
C:/data/msvc/14.22.27905/include\ostream(613): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const char *)'
C:/data/msvc/14.22.27905/include\ostream(658): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,char)'
C:/data/msvc/14.22.27905/include\ostream(694): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const char *)'
C:/data/msvc/14.22.27905/include\ostream(739): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,char)'
C:/data/msvc/14.22.27905/include\ostream(858): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const signed char *)'
C:/data/msvc/14.22.27905/include\ostream(864): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,signed char)'
C:/data/msvc/14.22.27905/include\ostream(870): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const unsigned char *)'
C:/data/msvc/14.22.27905/include\ostream(876): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,unsigned char)'
C:/data/msvc/14.22.27905/include\ostream(931): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const std::error_code &)'
(33): note: while trying to match the argument list '(std::ostream, T)'

        with

        [

            T=Cents

        ]

Compiler returned: 2

Remember what I said about crazy error messages? We hit the motherlode! Despite looking intimidating, these are actually quite straightforward. The first line is telling you that it couldn’t find an overloaded operator<< for the Cents class. All of the lines in the middle are all of the different functions it tried to match with but failed. The last error points out the function call that spawned this wall of errors.

Remember that average() returns a Cents object, and we are trying to stream that object to std::cout using the << operator. However, we haven’t defined the << operator for our Cents class yet. Let’s do that:

If we compile again, we will get another error:

c:test.cpp(14) : error C2676: binary '+=' : 'Cents' does not define this operator or a conversion to a type acceptable to the predefined operator

This error is actually being caused by the function template instance created when we call average(Cents*, int). Remember that when we call a templated function, the compiler “stencils” out a copy of the function where the template type parameters (the placeholder types) have been replaced with the actual types in the function call. Here is the function template instance for average() when T is a Cents object:

The reason we are getting an error message is because of the following line:

In this case, sum is a Cents object, but we have not defined the += operator for Cents objects! We will need to define this function in order for average() to be able to work with Cents. Looking forward, we can see that average() also uses the /= operator, so we will go ahead and define that as well:

Finally, our code will compile and run! Here is the result:

11 cents

If this seems like a lot of work, that’s really only because our Cents class was so bare-bones to start. The key point here is actually that we didn’t have to modify average() at all to make it work with objects of type Cents (or any other type). We simply had to define the operators used to implement average() for the Cents class, and the compiler took care of the rest!


13.3 -- Template classes
Index
13.1 -- Function templates

130 comments to 13.2 — Function template instances

  • Dong

    - In the code example, I copy and run in Code::Block. However, this code reports an error message. I clearly check the code again. I do not know why? Can you explain and solve this problem. Thank for you help.

    - Error: ||=== Build: Debug in template (compiler: GNU GCC Compiler) ===|
    D:\Book\utility\learnC++\template\main.cpp||In function 'int main()':|
    D:\Book\utility\learnC++\template\main.cpp|51|error: no matching function for call to 'average(Cents [4], int)'|
    D:\Book\utility\learnC++\template\main.cpp|39|note: candidate: 'template<class T> Cents average(Cents*, int)'|
    D:\Book\utility\learnC++\template\main.cpp|39|note:   template argument deduction/substitution failed:|
    D:\Book\utility\learnC++\template\main.cpp|51|note:   couldn't deduce template parameter 'T'|
    ||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

  • Petter Nybråten

    I was unfaithful and watched a video on fortran the other day. So when I saw

    I thought "sum not equal to length, what's that doing here??"... I don't know whether to feel ashamed or laugh at this.

    X 8

  • Asgar

    Hi. Just one question about templates not having to conform to the one-definition-rule in a given program. Since a template is not compiled directly and, instead, an instance of a template is created (which becomes a normal function) depending on the compiler discovering the actual type used in the code, doesn't that bring back the rule applicable for a normal function?

    I mean, if a header file containing a template function is included by multiple files in a program and two different files use a specific type (say, int), then does't the compiler create two instances of int-variant of the template for those files and then compile those two normal functions? How does the linker react when it sees 2 copies of a "now-normal" function?

  • Luiz

    In this function:

    Aren't we returning a dangling reference to an annonymous object here? I went back to chapter 7.4a and 8.14 but I couldn't figure out why it's okay to do this.

    nascardriver even commented on 7.4a explaining that the reference was already dead by the time the control got back to the caller:

    this really got me confused now

    • nascardriver

      In `returnByReference`, `5` is a local variable, it only lives until the end of `returnByReference`.
      In `max`, `x` and `y` are references to the caller's variables, ie. the caller of `max` owns these variables and `max` doesn't affect their lifetime at all. There are no temporary objects in `max`, everything is owned by the caller.

  • Mohammed

    Why is the first line of the  code in the first example of section "Another Example " is this

    ?
    Instead of this

    I didn't get the point.
    By the way thanks

  • Gabe

    So basically the only issue at hand when using templates is needing to define operator overloading whenever you want to pass user defined types?

  • Fan

    I have a question regarding function templates and external variables.

    Suppose I have a variable

    declared in sub.cpp, and I have a template

    in main.cpp, where the main function lives. I compiled the two files, but then the linker complains that it cannot resolve the symbol var. What went wrong?

    • nascardriver

      I can't reproduce your issue. Please post a minimal example that compiles, but causes the linker error.

      • Fan

        Asked here:

        https://stackoverflow.com/questions/61999756/linker-error-undefined-reference-in-a-function-template-that-uses-external-vari#

        seems a bug with an old compiler.

  • Ahmed Alkanaq

    Thank you for all the efforts you put into this awesome tutorial!

    I am not sure if I am missing something here:

    When we overload the `operator<<` in the example above:

    To my understanding, the sole reason of adding `const` is to prevent the function from altering the referenced variable, why would the compiler refuse to accept the overloaded operator without `const`?

  • Kurt Van Bever

    Hi there,

    My program runs fine, but it produces a warning that I can't get rid of.

    So there's a header file (with template function definition) and a source file (with template function forward declaration). This function gets called in file Main.cpp

    At first there were linking problems, so I added the explicit instantiation in Screen.cpp. With that added, it works fine, but it does produce the following warning :
    --function definition for display not found--
    and it puts a green wavy line under the identifier "display" in the explicit instantiation.

    Is there any way I can get rid of this warning ?

    Screen.h

    Screen.cpp

    Main.cpp

    • nascardriver

      I can't tell what's happening from the code you posted. As far as I can tell, you're trying to instantiate `display(const std::vector&)` in "Screen.h". That shouldn't work, because `display` hasn't been defined. Your error description doesn't fit the code.
      Please post a minimal compilable (or as close to compilable as you can get it) example along with the exact warning or error message. Remove any code that's unrelated to the problem.

    • Kurt Van Bever

      I tried to tidy it up a bit.
      I think this is the jist of it.
      All .h files are actually header guarded.

      Button.h

      Button.cpp

      List.h

      List.cpp

      Screen.h

      Screen.cpp

      Constants.h

      Main

      • nascardriver

        That's still too much code, and it doesn't compile, because there are missing headers.
        It should be possible to reproduce your issue with 2 .cpp files and no headers.
        Remove everything that's unrelated to the problem. Function bodies aren't required, members are required, values are required. Bring it down to the bare minimum. You said you can compile your code with a warning, so I expect code that I can compile with a warning too.

        Your problem should boil down to something like

        Screen.h

        Screen.cpp

        main.cpp

        Adjust this code such that it produces the same warning as before. Then post the updated code with the warning.

        • Kurt Van Bever

          OK so this works perfectly.
          The code prints:

          Exit   Log In    Log Out
          Press any key to continue . . .

          i'm using the latest version of Visual Studio with C++17

          Please note that the code doesn't produce any errors or warnings on the error list window.

          However : there is a squiggly green line under the identifier "display" on line 19 of Screen.h. When you hover your mouse over it, it will display a warning. The warning reads :

          --"Function definition for 'display' not found."--

          When I comment this line (line 19) out, then I get a linker error. so I guess the line is necessary, but I must be doing something wrong.

          Here is the code :

          Screen.h

          Screen.cpp

          Main.cpp

          • nascardriver

            That's something I can work with, thanks.
            This code shouldn't compile, because of Screen.h:19. In order to explicitly instantiate a template, the template must already have been defined. Make sure you have disabled language extensions. If you can still compile your code after that, it's a bug in msvc.

            To fix your issue, move the explicit instantiation (Screen.h:19) into Screen.cpp after you defined `display`.
            When you define a template function in a source file, you can only use the function with types that you explicitly instantiated the function with. If you want to be able to use the template function with all types, it needs to be defined in the header.

            • Kurt Van Bever

              Thanks for looking into this.

              I disabled the language extentions and moved the line to Screen.cpp.
              The result is still the same, the squiggly green line is still there.
              So is the warning.
              I must be using bad form but it's hard to find the proper way to do this.
              Maybe a 3 file approach like you do with template classes...

            • Kurt Van Bever

              It looks like the compiler is unable to decide whether to substitute <T> for [std::vector<SystemButton>] or for just [SystemButton]

              Let's try and use an alias first by changing (Screen.cpp:15&16) like this :

              Screen.cpp

              The problem is still there. The compiler still can't choose between the two possible types.
              So now let's change the types in the templates them selves.
              (Screen.h:17) & (Screen.cpp:9) like this :

              Screen.h

              Screen.cpp

              bingo!! The squiggly green line is gone and so is the warning. I guess I found the limits of what is possible and I imagine that in more complex code this could be a tough nut to crack.

              Still, there has to be a way, a method of good practice in c++ that is unknown to me, that addresses this kind of problem.

              I would like to hear about that.

  • It gives me errors. Please can you help me identify what's wrong?

    • Markzy

      you cant change const value in line 5, try x+1

    • Ayrton Fithiadi Sedjati

      Firstly, you haven't defined the function 'max()'.

      Secondly to my knowledge, if you pass 'x' by reference, the operation '++x' affects the actual 'x' variable and not a copy of it. Thus, you cannot do it when 'x' is passed as a const. Marking your function as const also prevents it from modifying the value of 'x'. Try removing both const keywords.

      Thirdly, I think, when you pass 'y' to your function, the compiler knows that 'typename T' must be a 'double'. However, since no explicit conversion is instructed in the function, then it would still return the type same type 'double'. It can't deduce what 'typename S' should be.

      If I understood you correctly, then this code should work for you:

  • hellmet

    Oh my god... Line 55 in the error message doesn't even exist in the code snippet! How do I debug these then!

  • Nirbhay

    Hello!

    In the code below:

    1.Why did not we make the 'operator+=' and 'operator/=' functions friend functions of class Cents?

    2. Also please explain what is happening here.

    -How do these two functions work?
    -Why is this returning *this here?

    Thanks!

    • Alex

      1) Lesson 9.4 has a table of when to use what forms of overloading. Binary operators that modify their left operand are typically implemented as member functions.
      2) These functions called in a case like "c1 += c2" where c1 and c2 are Cents objects. c1 becomes the implicit object, c2 becomes the cents parameter. The value of c2 is added into c1, and then a reference to c1 is returned so we can chain the result into another operator if we wish (e.g. c0 += c1 += c2).

  • Nirbhay

    Hello!

    I think if you add the "quoted" part in the sentence below, it would be easier for the readers.

    C++ will create a template instance for max() that looks like this:

    And then it will try to compile this function. See the problem here? C++ has no idea how to evaluate x > y! "because x and y are Cents class objects!".

  • andreas Serov

    Is there any reason why we dont pass cents as a reference in the following overloaded operator:

    like so:

  • Atas

    It's okay with VS compiler to have default template arguments that are not the rightmost ones:

    With function default arguments that wouldn't work, what's the difference?

    • You can specify the types when you're calling the function.

      You can't do this with default arguments

  • Udit

    Reformulated using vector.

  • Ashish Farande

    Hello, I am new to cpp. So, pardon me if my question seems very naive.

    In the following section of the code:

    we made the operator as a friend function because we needed to access the private member m_cents.

    Then, why didn't we make the following operators as the friend function, even though we are accessing the private members of the class.

  • lyf

    What's the difference of template <typename T> and template <class T>?

  • Spirit

    "We hit the motherload!" 'motherload' is a common mistake. The proper term is 'mother lode', which may be spelled as one word.

  • jasom

    Using templates gives me a TON of errors for no reason. I copy and pasted your code, and its still giving me errors. I spent hours over these errors, please help me !!!

    • * Line 29: Initialize your variables with brace initializers. You used copy initialization.
      * Line 14, 26, 27: Initialize your variables with brace initializers. You used direct initialization.
      * Line 11: Initialize your variables with brace initializers.

      There is no error in your code. Please post the errors you're seeing.

  • Truckracer

    When overloading the operators > and << you have declared the operator functions a friend of Cents class but defined them INSIDE the class. Is that a mistake?

  • There are several problems (aside @main being void).
    There is no function compatible with your call (in line 5), so the code won't compile. Even without templates, the same problem occurs.

    If you added another template parameter

    the call would work, but the return type is wrong.
    You have to decide on a type the @max returns. Either always @T or always @U, and add a cast in @max.

    If you cast @y to an int (or @x to double) in @main, @max is returning the address of a temporary. This isn't a problem as long as the return value is const (I don't understand why, but I can't seem to break it), but it isn't nice.

  • Hi Alex,

    Using your sample template:

    what would happen if you called it with mixed types say:

    Will the compiler implicitly convert the int to a double or will I need a static cast to do so?

    • I tried several times but the compiler just wasn't having it.  The only workaround was to declare them both as doubles and add .0 to the integer when declared.

      • Sorry, I accidentally posted a new comment instead of replying to yours. See my response here https://www.learncpp.com/cpp-tutorial/132-function-template-instances/comment-page-2/#comment-378452

        • Hi nascardriver,

          Yes, thanks, I saw your comment above and assumed it was in reply to mine.  

          Moving on, looking through the index I noted that there was no mention of using forms to enter data - variables or data sets of any type, so I have just spent some time finding a method to create forms in Visual Studio2017. I know that it is part of the C# package but not the standard C++ package.  I did find a method that works if anybody is interested in creating forms with buttons and empty text boxes here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/e6fbde42-d872-4ab3-8000-41ab22a4a584/visual-studio-2017-windows-forms after all, in the modern desktop environments what is the use of a program without forms?

          • Forms are OS specific and unrelated to the programming language used to create them. learncpp is about teaching the language, so I don't see a reason why Alex would include them in the tutorial.
            I don't know of a good framework to create GUIs, the best I know of is Qt, though I dislike it, because it's not pure C++ and requires a dedicated compiler.

            > what is the use of a program without forms?
            Being a Windows user you're used to have forms in every program.
            GUIs are just a way to make navigation easier or looks nicer. Many programs would work fine in a console environment too. Linux (especially on a server) can be used entirely without a desktop, just a plain console, like in the old days.
            Consumer software/technology has evolved enough to be pretty much unusable without GUI, so it's hard to imagine a computer without it, but it's possible.

            • "Many programs would work fine in a console environment too. Linux (especially on a server) can be used entirely without a desktop, just a plain console, like in the old days."

              Agreed, I whet my teeth on ms-dos and UNIX (both command OS's) and loved them both - I can remember trying a pre-release version of Windows - but for the modern market in the public domain I think many would struggle without a GUI.

              Think I am over complicating things for myself though currently....

Leave a Comment

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