A brief analysis of templates in C++

  • 2020-04-02 01:38:36
  • OfStack

1. What is a template

Suppose we now complete the function of x^2 + y^2 +x * y given two Numbers x and y. Considering that x and y may be int, float or double, we need to complete three functions:

Int fun (int x, int y);
Float fun (float x, float y);
Double fun (double x, double y);

And the operations to be done inside each fun function are also extremely similar. As follows:


int fun(int x,int y)
{
    int tmp = x *x + y * y + x * y;
    return tmp;
}
float fun(float x,float y)
{
    float tmp = x *x + y * y + x * y;
    return tmp;
}
double fun(double x,double y)
{
    double tmp = x *x + y * y + x * y;
    return tmp;
}

It can be seen that the above three function bodies are all the same except that they are of different types. How good would it be to write only one function to complete the functions of the above three functions? If you extract a generic function from these three functions, and it applies to these three different types of data, the code will be significantly more reusable. In fact, templates in C++ do exactly that. Templates enable parameterization of types (defining types as parameters), thereby achieving true code reusability. The template in C++ can be divided into function template and class template, and the function of the template is called template function, the template of the concrete into the template class. Let's take a look at what are function templates and class templates respectively ~~~

2. Template functions

In fact, we use the function template, only need a function may complete the above three functions, thousands of words than to see the code:


#include <iostream>
using namespace std;
template <typename T>
T fun(T x,T y)
{
    T tmp = x *x + y * y + x * y;
    return tmp;
}
int main()
{
    int x1 = 1,y1 = 4;
    float x2 = 1.1 , y2 = 2.2;
    double x3 = 2.0 , y3 = 3.1;
    cout<<fun(x1,y1)<<endl;
    cout<<fun(x2,y2)<<endl;
    cout<<fun(x3,y3)<<endl;
    return 0;
}

Operation results:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201309/201309241007159.jpg ">

By doing this, we were able to achieve our goals easily, and this greatly improved the reusability of the code, which reminded us of the algorithms in the STL that use multiple data types. In fact, STL is an important application of templates.

Now let's think, what if the code above calls fun(x1,y2) this way? Clicking compile causes an error like this:

You can see that the problem with the compilation is fun(x1,y2), which means that there is no corresponding function, either x1 and y2 are both int, or x1 and y2 are both float. So why do I say that? The main purpose is to elicit the template can also be used at the same time:


#include <iostream>
using namespace std;

template <typename T1 , typename T2>
T2 fun(T1 x,T2 y)
{
    T2 tmp = x *x + y * y + x * y;
    return tmp;
}
int main()
{
    int x1 = 1,y1 = 4;
    float x2 = 1.1 , y2 = 2.2;
    double x3 = 2.0 , y3 = 3.1;
    cout<<fun(x1,y1)<<endl;
    cout<<fun(x2,y2)<<endl;
    cout<<fun(x3,y3)<<endl;
    cout<<fun(x1,y2)<<endl;
    return 0;
}

Operation results:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201309/2013092410071511.jpg ">

Why does fun(x1,y1) work correctly when using two templates? Because when you make this call, T1 = int, T2 = int. So this call is fine.

It's natural to think of overloading functions, but can template functions be overloaded? Obviously yes, or look at the code:


#include <iostream>
using namespace std;

template <typename T1 , typename T2>
T2 fun(T1 x,T2 y)
{
    cout<<" Called with two arguments  fun  function  ^^ "<<endl;
    T2 tmp = x *x + y * y + x * y;
    return tmp;
}
template <typename T>
T fun(T x , T y , T z)
{
    cout<<" With three arguments called  fun  function  ^^ "<<endl;
    T tmp = x * x + y * y + z * z + x * y * z; 
    return tmp;
}
int main()
{
    int x1 = 1 , y1 = 4 , z1 = 5;
    float x2 = 1.1 , y2 = 2.2;
    double x3 = 2.0 , y3 = 3.1;
    cout<<fun(x1,y1)<<endl;
    cout<<fun(x2,y2)<<endl;
    cout<<fun(x3,y3)<<endl;
    cout<<fun(x1,y2)<<endl;
    cout<<fun(x1,y1,z1)<<endl;
    return 0;
}

Operation results:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201309/2013092410071512.jpg ">

The result is that there is no problem with overloading template functions. Is it possible to overload between template and non-template functions?


#include <iostream>
using namespace std;
template <typename T>
T fun(T x,T y)
{
    cout<<" The template function is called  ^^ "<<endl;
    T tmp = x * x + y * y + x * y;
    return tmp;
}
int fun(int x,int y)
{
    cout<<" Non-template functions are called  ^^ "<<endl;
    int tmp = x * x + y * y + x * y;
    return tmp;
}
int main()
{
    int x1 = 1 , y1 = 4;
    float x2 = 1.1 , y2 = 2.2;
    cout<<fun(x1,y1)<<endl;
    cout<<fun(x2,y2)<<endl;
    return 0;
}

Operation results:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201309/2013092410071513.jpg ">

To see that template and non-template functions can be overloaded, what is the order in which overloaded functions are called? In fact, it is to find the non-template function first, to have a strict match of the non-template function, call the non-template function, can not find a suitable non-template function in the template function to match.

So much for templates ~~~~

3. The template class

If you understand the template function, the template class is quite simple, but the template function is to use the template for the type in the function, and the template class is to use the template for the type in the class, I will not say more, the following code is I used to use the template to write a single linked list, this is a typical application of the template :(tested)


#include <stdio.h>
#include <iostream.h>
template <class T>
struct SLNode
{
    T data;
    SLNode<T> *next;
    SLNode(SLNode<T> *nextNode=NULL)
    {
        next = nextNode;
    }
    SLNode(const T &item,SLNode<T> *nextNode=NULL)
    {
        data = item;
        next = nextNode;
    }
};
template <class T>
class SLList
{
private:
    SLNode<T> *head;
    SLNode<T> *tail;
    SLNode<T> *currptr;
    int size;
public:
    SLList();
    SLList(const T &item);
    ~SLList();
    bool IsEmpty()const;
    int Length()const;
    bool Find(int k,T &item)const;
    int Search(const T &item)const;
    void InsertFromHead(const T &item);
    void InsertFromTail(const T &item);
    bool DeleteFromHead(T &item);
    bool DeleteFromTail(T &item);
    void Insert(int k,const T &item);
    void Delete(int k,T &item);
    void ShowListMember();
};
//The constructor
template <class T>
SLList<T>::SLList()
{
    head = tail = currptr = new SLNode<T>();
    size = 0;
}
//The constructor
template <class T>
SLList<T>::SLList(const T &item)
{
    tail = currptr = new SLNode<T>(item);
    head = new SLNode<T>(currptr);
    size = 1;
}
//The destructor
template <class T>
SLList<T>::~SLList()
{
     SLNode<T> *temp;
    while(!IsEmpty())
    {
        temp = head->next;
        head->next = temp->next;
        delete temp;

    }
}
//Determines if the list is empty
template <class T>
bool SLList<T>::IsEmpty()const
{
    return head->next == NULL;
}
//Returns the length of the list
template <class T>
int SLList<T>::Length()const
{
     return size;
}
//Find the threshold for the KTH node
template <class T>
bool SLList<T>::Find(int k,T &item)const
{
    if(k < 1)
    {
        cout<<"illegal position !"<<endl;
    }
    SLNode<T> *temp = head;
    int count = 0;
    while(temp != NULL && count < k)
    {
        temp = temp->next;
        count++;
    }
    if(temp == NULL)
    {
        cout<<"The list does not contain the K node !"<<endl;
        return false;
    }
    item = temp->data;
    return true;
}
//Find the data threshold for which item is the number of elements in the table
template <class T>
int SLList<T>::Search(const T &item)const
{
    SLNode<T> *temp = head->next;
    int count = 1;
    while(temp != NULL && temp->data != item)
    {
        temp = temp->next;
        count++;
    }
    if(temp == NULL)
    {
        cout<<"The node does not exist !"<<endl;
        return -1;
    }
    else
    {
        return count;
    }
}
//Insert from the header
template <class T>
void SLList<T>::InsertFromHead(const T &item)
{    
    if(IsEmpty())
    {
        head->next = new SLNode<T>(item,head->next);
        tail = head->next;
    }
    else
    {
        head->next = new SLNode<T>(item,head->next);
    }
    size++;
}
//Insert from the end of the table
template <class T>
void SLList<T>::InsertFromTail(const T &item)
{
    tail->next = new SLNode<T>(item,NULL);
    tail = tail->next;
    size++;
}
//Delete from the header
template <class T>
bool SLList<T>::DeleteFromHead(T &item)
{
    if(IsEmpty())
    {
        cout<<"This is a empty list !"<<endl;
        return false;
    }
    SLNode<T> *temp = head->next;
    head->next = temp->next;
    size--;
    item = temp->data;
    if(temp == tail)
    {
        tail = head;
    }
    delete temp;
    return true;
}
//Delete from the end of the table
template <class T>
bool SLList<T>::DeleteFromTail(T &item)
{
    if(IsEmpty())
    {
        cout<<"This is a empty list !"<<endl;
        return false;
    }
    SLNode<T> *temp = head;
    while(temp->next != tail)
    {
        temp = temp->next;
    }
    item = tail->data;
    tail = temp;
    tail->next=NULL;
    temp = temp->next;
    delete temp;
    size--;
    return true;
}
//Insert the item value after the KTH node
template <class T>
void SLList<T>::Insert(int k,const T &item)
{
    if(k < 0 || k > size)
    {
        cout<<"Insert position Illegal !"<<endl;
        return;
    }
    if(k == 0)
    {
        InsertFromHead(item);
        return;
    }
    if(k == size)
    {
        InsertFromTail(item);
        return;
    }
    SLNode<T> *temp = head->next;
    int count = 1;
    while(count < k)
    {
        count++;
        temp = temp->next;
    }
    SLNode<T> *p = temp->next;
    temp->next = new SLNode<T>(item,p);
    size++;
}
//Delete the value of the KTH node and save it in the item
template <class T>
void SLList<T>::Delete(int k,T &item)
{
    if(k <= 0 || k > size)
    {
        cout<<"Ileegal delete position !"<<endl;
        return;
    }
    if(k == 1)
    {
        DeleteFromHead(item);
        return;
    }
    if(k == size)
    {
        DeleteFromTail(item);
        return;
    }
    SLNode<T> *temp = head->next;
    int count = 1;
    while(count < k-1)
    {
        count++;
        temp = temp->next;
    }
    SLNode<T> *p = temp->next;
    temp->next = p->next;
    p->next = NULL;
    item = p->data;
    delete p;
    size--;
}
template <class T>
void SLList<T>::ShowListMember()
{
    cout<<"List Member : ";
    SLNode<T> *temp = head->next;
    while(temp != NULL)
    {
        cout<<temp->data<<" ";
        temp = temp->next;
    }
    cout<<endl;
}
/*
1. The introduction of the InsertFronHead,InsertFromTail,DeleteFromHead and DeleteFromTail Used to implement Insert and Delete Delta function is a good way to do it. 
2.SLNode(T &item,SLNode<T> *nextNode) this The constructor It is cleverly designed to facilitate the implementation of other member functions. 
3. Insert, delete into: table head, table tail, middle insert (delete) three cases 
*/
int main()
{
    int item;
    SLList<int> list(12);
    list.Insert(0,11);
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();
    list.Insert(2,14);
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();
    list.Insert(2,13);
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();
    list.Delete(2,item);
    cout<<"item = "<<item<<endl;
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();
    list.Delete(1,item);
    cout<<"item = "<<item<<endl;
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();
    list.Delete(2,item);
    cout<<"item = "<<item<<endl;
    cout<<"list number:"<<list.Length()<<endl;
    list.ShowListMember();
    return 0;
}

The advantage of using templates is that the data in SLList can be any data type, which is the concept of generic programming


Related articles: