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:


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!


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





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)'





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
13.1 -- Function templates

98 comments to 13.2 — Function template instances

  • 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


    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?


    • 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


    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

        • 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: 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]