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 &;gt; 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!

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

31 comments to 13.2 — Function template instances

  • MarkG

    Hi Alex,

    If I use

    as my << overide definition within the Cents class, then the line

    in the main function produces an error in my compiler (see below), which I
    think is related to the chaining of the << operator.

    error: no match for ‘operator<<’ in ‘std::cout << Average [with T = Cents](((Cents)(& cArray)), 4)’

    If the Average function returned a *this pointer I think this would solve
    the problem (alternatively, I can introduce a dummy Cents variable to
    store the result of Average, and std::cout that). However, another way to
    resolve this problems is to rewrite the << override function with a const
    input for Cents, like so:

    Now the line

    compiles without a problem! Can you help me understand why the addtion of
    the const keyword to the input resolves the problem of chaining the output
    of the Average function for Cents? Also, would it be possible to modify the
    template Average function so that for classes it returned a *this pointer
    rather than a value? I suspect not as the function is outside of the Cents
    class, but would be interested to know. This appears to be the same problem
    as Ben (post #1) had.

    Thanks,

    Mark

    P.S. Fantastic tutorial by the way!

    • First off, I changed the example to use a const Cents &cents, which it should have been all along, because operator<< is not modifying the Cents it’s using.

      As to why your example didn’t work, let’s start with a simpler one. Consider the following statement:

      Would you expect this to work if operator<< didn’t take a const Cents reference?

      The answer should be no. Cents(5) is an rvalue, and does not have it’s own address. Thus, you can’t set a reference to it.

      Because Average() is returning a Cents by value, it’s falling into the same trap. It’s the exact same reason the following doesn’t work:

      However, if you make the reference const, then you CAN pass in literals and temporaries.

      and similarly, the return-by-value Cents from Average works as well.

      As to your second question, you can’t return *this from Average because it’s not a member function.

  • AndiW

    Hi Alex,

    excellent tutorial! I found a little typo though. The
    Last return statement in the last block of code in the
    “Operators, function calls, and function templates” is

    but it should be

    Also, the return type shouldn’t be bool, but Cents.
    Otherwise it does not fit the max-template. Right?

    Thanks!

    Andreas

    • Andreas, I believe the code is correct as written. We’re not trying to fit the max-template here, but rather we’re overloading the > operator so that the compiler knows how to do c1 > c2 when it encounters that inside the max() template for Cents.

      Consider the logical meaning of the following statment:

      if the > operator returns a bool here, then c1 > c2 returns a bool here and this makes sense. But if it returns a Cents instead, what does that mean logically?

  • Hi Alex,

    could you tell me why there is a friend for the << operator?

    Thanks

    • Conrado

      Read http://www.learncpp.com/cpp-tutorial/92-overloading-the-arithmetic-operators/ to understand the use of “friend function” to overload an operator.

  • Prashant Patel

    what is difference between template <typename T> and template <class T>.
    Form me both are genrating same result.

    • vamsi

      Prashant. Better to use 'typename' for “function template” and 'class' for “class template”

    • Alex

      The only difference is that you must use “class” instead of “typename” when doing templates of templates (and I think this restriction will be relaxed in a future version of C++).

      Otherwise they are identical.

  • DOBRESCU Mihai

    Hi,

    For the first part, maybe it would be clearer for the reader if a sample program were provided. Here is the code:


    #include

    template // this is the template parameter declaration
    Type max(Type tX, Type tY)
    {
    return (tX > tY) ? tX : tY;
    }

    using namespace std;

    class Cents
    {
    private:
    int m_nCents;
    string m_sName;
    public:
    Cents(int nCents, string sName)
    : m_nCents(nCents), m_sName(sName)
    {
    }

    int GetCents()
    {
    return m_nCents;
    }

    string GetName()
    {
    return m_sName;
    }

    friend bool operator>(Cents &c1, Cents&c2)
    {
    return (c1.m_nCents > c2.m_nCents) ? true: false;
    }
    };

    int main()
    {
    Cents cNickle(5, "A nickle");
    Cents cDime(10, "A dime");

    Cents cBigger = ::max(cNickle, cDime);

    cout << cBigger.GetName() << " is bigger." << endl;

    return 0;
    }

    And here is the output:


    A dime is bigger.

    Also, apparently the easiest way to get rid of the namespace collision is to prefix the function name with the scope resolution operator (“::”), so that the std namespace is avoided and the global namespace is used in order to search for the function:


    Cents cBigger = ::max(cNickle, cDime);

    Again, it would be easier to understand for the reader if the second example also had listed a full sample program, like in the following example:


    #include

    using namespace std;

    class Cents
    {
    private:
    int m_nCents;
    public:
    Cents(int nCents)
    : m_nCents(nCents)
    {
    }

    int GetCents()
    {
    return m_nCents;
    }

    friend ostream& operator<< (ostream &out, const Cents &cCents)
    {
    out << cCents.m_nCents << " cents ";
    return out;
    }

    void operator+=(Cents cCents)
    {
    m_nCents += cCents.m_nCents;
    }

    void operator/=(int nValue)
    {
    m_nCents /= nValue;
    }
    };

    template
    T Average(T *atArray, int nNumValues)
    {
    T tSum = 0;
    for (int nCount=0; nCount < nNumValues; nCount++)
    tSum += atArray[nCount];

    tSum /= nNumValues;
    return tSum;
    }

    int main()
    {
    int anArray[] = { 5, 3, 2, 1, 4 };
    cout << Average(anArray, 5) << endl;

    double adArray[] = { 3.12, 3.45, 9.23, 6.34 };
    cout << Average(adArray, 4) << endl;

    Cents cArray[] = { Cents(5), Cents(10), Cents(15), Cents(14) };
    cout << Average(cArray, 4) << endl;

    return 0;
    }

    And its output:


    3
    5.535
    11 cents

    The question that still remains is the following one: why did not the compiler complain when it encountered the following line:


    T tSum = 0;

    That is, what happens when the following assignment takes place?


    Cents tSum = 0;

    Also, templating resembles a little bit preprocessing. Does it happen like in preprocessing? Is the text replaced somewhere with other excerpts of text, that are subsequently compiled? Or, how otherwise do the function template instances get compiled into binary code? I assume that there must be some temporary text files somewhere that contain the function template instances.

  • luke

    Hi,

    what the meaning of

    Cents tSum = 0;

    implicit convert from int to Cents?

  • nikita

    hey alex,

    template<typename type>
    and
    template<class type>

    whats the difference?

    • Alex

      They’re largely identical.

      Scott Myers recommends using “class” when it is expected that the type will always be a class, and “typename” if other types (such as int or char*) may be expected.

      One reason to prefer “class” over “typename” is because class will work with template template arguments, whereas typename will not. I presume this will be fixed in the next version of C++, but as of C++14 it’s still not.

      Personally, I just always use class.

  • Reaversword

    It wouldn’t be smarter/quicker (for this concrete case) to simply add a typecast from Cents to Ints?

    operator int() { return m_nCents; }//(Because is m_nCents type)

    • Reaversword

      …or there is some downside am I overlooking?

      • Alex

        For what purpose are you thinking this would be beneficial?

        • Reaversword

          Well, I guess my point here was not recreate all operator definitions, just do only the typecast and let C++ deal with operators using typecasting.

          But I don’t know if doing this implies some downside!.

          Suppose you’ll need to overload 20 operators. Would then just make a single typecast overload much quicker?

          • Reaversword

            Wouldn’t be then just make a single typecast overload much quicker? (I forgot the verb!)

            • Alex

              The verb you’re looking for is “type conversion operator”.

              This is a suitable way to proceed for cases where you don’t want to define an overloaded operator when a standard operator will do.

  • Loungani

    #include<iostream>
    using namespace std;
    template<typename Type>
    Type Max(Type tX,Type tY)
    {return (tX>tY)?tX:tY;}
    class Cents
    {
    private:
        int m_nCents;
    public:
        Cents(int nCents)
        : m_nCents(nCents)
        {
        }
        
        friend bool operator>(Cents &c1, Cents&c2)
        {
            return (c1.m_nCents > c2.m_nCents) ? true: false;
        }
    };
    int main()
    {
        Cents cNickle(5);
        Cents cDime(10);
        Cents cBigger=Max(cNickle, cDime);
        std::cout<<cBigger;

    I removed it but it is giving me error  message Invalid operands to binary expression (‘ostream’ (aka ‘basic_ostream<char>’) and ‘Cents’)

  • BrainBoom

    Hi everyone, if you found this code no run normally in your computer as you expect,
    because ostream is in the namespace std, don’t forget it.

    Hey Alex,

    Thanks~

    • Alex

      Yes, in this case the compiler will try to convert integer 0 to a Cents object, which it will be able to do via the Cents(int) constructor, and then this will be copied into cCents. Although this may seem less performant that doing a direct initialization (e.g. Cents cCents(0)), your compiler will probably elide the copy (prevent it) and just initialize cCents directly.

  • ram joshi

    hi Alex,
    Want to know that when Cents cBigger = max(cNickle, cDime) line is executed will copy constructor invoke to initialize cBigger ?  if not then how cBigger will initialize?

    • Alex

      (Edit: When this question was asked and answered, max() was returning by value. I’ve since changed max to return a const reference)

      Yes, since function max() is returning a class by value, it will use the copy constructor to do so. However, note that because we’re using this value as an initializer in this case, the copy constructor may be elided.

  • Vijeet Singh

    why is the body of a friend function follows the declaration immediately here, and its outside the class everywhere else

    shouldn’t it be more like this? ---

    friend bool operator>(cents &c1, cents &c2)
    };
    //class ends here
    bool operator<(cents &c1, cents &c2)  //actual body of method outside the class
    {
    return(c1.m_nCents > c2.m_nCents)
    }

    same question while overlaoding operator<<

  • Vijeet Singh


    #include "stdafx.h"
    #include<stdio.h>
    #include<iostream>
    using namespace std;

    template<class T1>
    class player
    {
    private:
        T1 score;
    public:

        player(T1 n){score =n;}
        friend ostream& operator<<(ostream& cout, player <T1> p);
        
    };
    template <class T1>
    ostream& operator<<(ostream& cout, player <T1> p)
        {cout<<p.score;return cout;}

    void main()
    {
        player<int> p1(3);
            cout<<p1;
        system("pause");
        }

    when a << operator is overloaded for template class object, the body of the friend function must follow the declaration immediately. If I write the body outside the class like above, it gives error LNK1120 and LNK2019, linking errors.
    whereas, if the class is not template, it doesn’t matter whether the body of operator overloading friend function is inside or outside the class, like you said "either way works"

    is there any way by which we can overload << operator in template class by writing the body of the function outside the class(its working when I write it inside the template class)

    • Alex

      This topic gets complex fast.

      Short answer:

      Explanation: read this article

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter