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++ has no idea how to evaluate x > y! 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!

c:\consoleapplication1\main.cpp(55): error C2679: binary '<<': no operator found which takes a right-hand operand of type 'Cents' (or there is no acceptable conversion)
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(497): note: could be 'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_streambuf> *)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(477): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(const void *)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(457): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(long double)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(437): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(double)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(417): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(float)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(396): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned __int64)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(376): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(__int64)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(355): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned long)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(335): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(long)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(315): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned int)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(290): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(int)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(270): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(unsigned short)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(236): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(short)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(216): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(bool)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(209): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(std::ios_base &(__cdecl *)(std::ios_base &))'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(202): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_ios> &(__cdecl *)(std::basic_ios> &))'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(196): note: or       'std::basic_ostream> &std::basic_ostream>::operator <<(std::basic_ostream> &(__cdecl *)(std::basic_ostream> &))'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(694): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const char *)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(741): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,char)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(779): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const char *)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(826): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,char)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(952): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const signed char *)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(959): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,signed char)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(966): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const unsigned char *)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(973): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,unsigned char)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(983): note: or       'std::basic_ostream> &std::operator <<,T>(std::basic_ostream> &&,const _Ty &)'
1>          with
1>          [
1>              T=Cents,
1>              _Ty=Cents
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\ostream(1021): note: or       'std::basic_ostream> &std::operator <<>(std::basic_ostream> &,const std::error_code &)'
1>  c:\consoleapplication1\main.cpp(55): note: while trying to match the argument list '(std::ostream, Cents)'

Remember what I said about crazy error messages? We hit the motherload! 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

73 comments to 13.2 — Function template instances

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