C++ Template application details

  • 2020-05-10 18:32:42
  • OfStack

The introduction

Template (Template) refers to the programming of C++ programming design language which takes the type as the parameter and supports the general programming. The standard library of C++ provides many useful functions, most of which combine the idea of templates, such as STL and IO Stream.

A function template

In the introduction to c++, many people will be exposed to functions such as swap(int&, int&), which have the following code:


void swap(int&a , int& b) {
  int temp = a;
  a = b;
  b = temp;
}

However, if we want to support long,string and custom class swap functions, the code is similar to the above code, but of different types. At this time, we define swap function templates, and we can reuse different types of swap function codes. The declaration form of function templates is as follows:


template <class identifier> function_declaration;
template <typename identifier> function_declaration;

The declaration and definition code of swap function template are as follows:


//method.h
template<typename T> void swap(T& t1, T& t2);

#include "method.cpp"

//method.cpp

template<typename T> void swap(T& t1, T& t2) {
  T tmpT;
  tmpT = t1;
  t1 = t2;
  t2 = tmpT;
}

The above is the declaration and definition of the template. How to instantiate the template? The instantiation of the template is something the compiler does, and has nothing to do with the programmer.


//main.cpp
#include <stdio.h>
#include "method.h"
int main() {
  // Template method  
  int num1 = 1, num2 = 2;
  swap<int>(num1, num2);
  printf("num1:%d, num2:%d\n", num1, num2); 
  return 0;
}

The swap function used here must contain the definition of swap, otherwise the compilation will be wrong, which is not the same as the 1-like function. So you must add #include "method.cpp" to the last line of the method.h file.

Class template

Consider us to write a simple stack class, the stack can support int type, long type, string type, etc., not using class template, we're going to write more than three stack classes, including basic 1 sample code, through the class template, we can define a simple stack template, according to need to instance into int stack as long stack, string stack.


//statck.h
template <class T> class Stack {
  public:
    Stack();
    ~Stack();
    void push(T t);
    T pop();
    bool isEmpty();
  private:
    T *m_pT;    
    int m_maxSize;
    int m_size;
};

#include "stack.cpp"

//stack.cpp
template <class T> Stack<T>::Stack(){
  m_maxSize = 100;   
  m_size = 0;
  m_pT = new T[m_maxSize];
}
template <class T> Stack<T>::~Stack() {
  delete [] m_pT ;
}
    
template <class T> void Stack<T>::push(T t) {
  m_size++;
  m_pT[m_size - 1] = t;
  
}
template <class T> T Stack<T>::pop() {
  T t = m_pT[m_size - 1];
  m_size--;
  return t;
}
template <class T> bool Stack<T>::isEmpty() {
  return m_size == 0;
}

The above definition of a class template - stack, this stack is very simple, just to explain how to use the class template, can only support a maximum of 100 elements to push, using the following example:


//main.cpp
#include <stdio.h>
#include "stack.h"
int main() {
  Stack<int> intStack;
  intStack.push(1);
  intStack.push(2);
  intStack.push(3);
  
  while (!intStack.isEmpty()) {
    printf("num:%d\n", intStack.pop());
  }
  return 0;
}

Template parameter

Templates can have type parameters, normal type parameters int, or default template parameters, for example


template<class T, T def_val> class Stack{...}

The stack of the above class template has one limitation, that is, it can only support 100 elements at most. We can use the template parameter to configure the maximum number of elements on the stack. If it is not configured, the default maximum value is set to 100.


//statck.h
template <class T,int maxsize = 100> class Stack {
  public:
    Stack();
    ~Stack();
    void push(T t);
    T pop();
    bool isEmpty();
  private:
    T *m_pT;    
    int m_maxSize;
    int m_size;
};

#include "stack.cpp"

template <class identifier> function_declaration;
template <typename identifier> function_declaration;
0

Use examples are as follows:


//main.cpp
#include <stdio.h>
#include "stack.h"
int main() {
  int maxsize = 1024;
  Stack<int,1024> intStack;
  for (int i = 0; i < maxsize; i++) {
    intStack.push(i);
  }
  while (!intStack.isEmpty()) {
    printf("num:%d\n", intStack.pop());
  }
  return 0;
}

Template specialization

  when we want to define different implementations of templates, we can use specialization of templates. For example, we defined the stack class template. If it is a stack of char* type, we hope to copy all the data of char into stack class, because only char pointer is saved, the memory pointed to by char pointer may be invalid, and the memory pointed to by stack pop-up stack element char pointer may be invalid. There is also the swap function template defined by us. When the container type is vector or list, if the object saved by the container is very large, it will take up a lot of memory and the performance will be degraded, because to generate a temporary large object to save a, all of these require the template specialization to solve.

Function template specialization

So let's say that our swap function is dealing with a situation where we have two elements of vector < int > When using the original swap function, execute tmpT = t1 to copy all the elements of t1, which takes up a large amount of memory, resulting in performance degradation, so our system USES vector.swap function to solve this problem, the code is as follows:


//method.h
template<class T> void swap(T& t1, T& t2);

#include "method.cpp"

template <class identifier> function_declaration;
template <typename identifier> function_declaration;
3

template < > The prefix indicates that this is a specialization, which is described without the template parameter, as shown in the following example:


template <class identifier> function_declaration;
template <typename identifier> function_declaration;
4

vector < int > If you want to use template specialization to solve all vector's swap, what should you do


template <class identifier> function_declaration;
template <typename identifier> function_declaration;
5

Instead of


template <class identifier> function_declaration;
template <typename identifier> function_declaration;
6

That's it. The rest of the code stays the same.

Class template specialization

For  , see the compare code below:


//compare.h
template <class T>
 class compare
 {
 public:
 bool equal(T t1, T t2)
 {
    return t1 == t2;
 }
};

#include <iostream>
#include "compare.h"
 int main()
 {
 using namespace std;
 char str1[] = "Hello";
 char str2[] = "Hello";
 compare<int> c1;
 compare<char *> c2;  
 cout << c1.equal(1, 1) << endl;    // Compare the two int Type parameter 
 cout << c2.equal(str1, str2) << endl;  // Compare the two char * Type parameter 
 return 0;
 }

When comparing two integers, compare's equal method is correct, but compare's template parameter is char*, this template will not work, so it is modified as follows:


template <class identifier> function_declaration;
template <typename identifier> function_declaration;
9

The main.cpp file is unchanged, and this code works fine.

Template type conversion

Remember our custom Stack template? In our program, let's say we defined Shape and Circle classes. The code is as follows:


//shape.h
class Shape {

};
class Circle : public Shape {
};

And then we want to use it this way:


//main.cpp
#include <stdio.h>
#include "stack.h"
#include "shape.h"
int main() {
  Stack<Circle*> pcircleStack;
  Stack<Shape*> pshapeStack;
  pcircleStack.push(new Circle);
  pshapeStack = pcircleStack;
  return 0;
}

You cannot compile here because Stack < Shape* > Not Stack < Circle* > However, we want the code to work like this, so we need to define the conversion operator. The Stack code is as follows:


//statck.h
template <class T> class Stack {
  public:
    Stack();
    ~Stack();
    void push(T t);
    T pop();
    bool isEmpty();
    template<class T2> operator Stack<T2>();
  private:
    T *m_pT;    
    int m_maxSize;
    int m_size;
};

#include "stack.cpp"

template <class T> Stack<T>::Stack(){
  m_maxSize = 100;   
  m_size = 0;
  m_pT = new T[m_maxSize];
}
template <class T> Stack<T>::~Stack() {
  delete [] m_pT ;
}
    
template <class T> void Stack<T>::push(T t) {
  m_size++;
  m_pT[m_size - 1] = t;
  
}
template <class T> T Stack<T>::pop() {
  T t = m_pT[m_size - 1];
  m_size--;
  return t;
}
template <class T> bool Stack<T>::isEmpty() {
  return m_size == 0;
}

template <class T> template <class T2> Stack<T>::operator Stack<T2>() {
  Stack<T2> StackT2;
  for (int i = 0; i < m_size; i++) {
    StackT2.push((T2)m_pT[m_size - 1]);
  }
  return StackT2;
}

//main.cpp
#include <stdio.h>
#include "stack.h"
#include "shape.h"
int main() {
  Stack<Circle*> pcircleStack;
  Stack<Shape*> pshapeStack;
  pcircleStack.push(new Circle);
  pshapeStack = pcircleStack;
  return 0;
}

In this way, Stack < Circle > Or Stack < Circle* > You can automatically convert to Stack < Shape > Or Stack < Shape* > , if the type of the conversion is Stack < int > To Stack < Shape > , the compiler will report an error.

other

1 class has no template parameters, but the member function has template parameters, which is feasible. The code is as follows:


class Util {
  public:
    template <class T> bool equal(T t1, T t2) {
      return t1 == t2;
    }
};

int main() {
  Util util;
  int a = 1, b = 2;
  util.equal<int>(1, 2);
  return 0;
}

You can even declare equal of Util as static. The code is as follows:


class Util {
  public:
     template <class T> static bool equal(T t1, T t2) {
      return t1 == t2;
    }
};

int main() {
  int a = 1, b = 2;
  Util::equal<int>(1, 2);
  return 0;
}

Related articles: