C++ function template and class template instance resolution

  • 2020-04-02 02:37:19
  • OfStack

This paper analyzes the C++ function template and class template in detail, which is helpful for readers to deepen their understanding of C++ function template and class template. The specific contents are as follows:

Generic Programming is a Programming paradigm that works by parameterizing types to manipulate multiple data types on the same piece of code. Generics are Generic and reusable. Generic programming originated in C++ to implement the C++ STL (standard template library).

A template is the basis for generic programming. A template is a blueprint or formula for creating a class or function. For example, when using a generic type like a vector or a generic function like find, we provide enough information to transform the blueprint into a specific class or function.

One, function template

A generic function template is a formula that can be used to generate a version of a function for a particular type or value. The template definition starts with the keyword template, followed by a list of template parameters separated by commas. The template parameter represents the type or value used in the class or function definition.

1. Type parameters

A template type parameter represents a type. We can think of a type parameter as a type specifier, just like a built-in type or class type specifier. The keyword class or typename must be used before a type parameter:


template <typename T> //Typename is the same as class
T function(T* p) 
{ 
  T tmp = *p;  //The temporary variable is of type T
  //... 
  return tmp;  //The return value type is T
} 

The keyword typename serves the same purpose as class, but typename is obviously more intuitive than class because it more clearly indicates that the subsequent name is a typename.

The compiler USES a template type argument to instantiate a specific version of the function for us, and a version is called an instance of the template. When we call a function template, the compiler usually USES function arguments to infer template arguments for us. Of course, if the function has no parameters of template type, we need to specify:


int a = 10; 
cout << function(&a) << endl;   //The compiler infers template arguments from function arguments
 
cout << function<int>(&a) << endl;  //<Int> Indicates that the template parameter is int

2. Non-type parameters

You can also define a nontype parameter in a template, where a nontype parameter represents a value rather than a type. We specify non-type parameters by a specific typename instead of the keyword class or typename:


//Plastic template
template<unsigned M, unsigned N> 
void add() 
{ 
  cout<< M+N << endl; 
} 
 
//Pointer to the
template<const char* C> 
void func1(const char* str) 
{ 
  cout << C << " " << str << endl; 
} 
 
//reference
template<char (&R)[9]> 
void func2(const char* str) 
{ 
  cout << R << " " << str << endl; 
} 
 
//A function pointer
template<void (*f)(const char*)> 
void func3(const char* c) 
{ 
  f(c); 
} 
 
void print(const char* c) { cout << c << endl;} 
 
char arr[9] = "template";  //Global variable with a static lifetime
 
int main() 
{ 
  add<10, 20>(); 
  func1<arr>("pointer"); 
  func2<arr>("reference"); 
  func3<print>("template function pointer"); 
  return 0; 
} 

When instantiated, non-type parameters are replaced by values provided by a user or inferred by the compiler. A non type parameters can be an integer, or a pointer to the object or function or reference: binding to plastic (not type parameters) of the argument must be a constant expression, is bound to a pointer or reference type (not parameters) arguments must have a lifetime of static (such as a global variable), not the ordinary local variables or dynamic object to bind to a pointer or reference of the type parameter.

Two, class template

Accordingly, the class template is used to generate the blueprint for the class. Unlike function templates, the compiler cannot infer template parameter types for class templates, so we must explicitly provide template arguments. Like function templates, class template parameters can be either type parameters or non-type parameters, which I won't repeat here.


template<typename T> 
class Array { 
public: 
  Array(T arr[], int s); 
  void print(); 
private: 
  T *ptr; 
  int size; 
}; 
 
//Class templates define member functions externally
template<typename T> 
Array<T>::Array(T arr[], int s) 
{ 
  ptr = new T[s]; 
  size = s; 
  for(int i=0; i<size; ++i) 
    ptr[i]=arr[i]; 
} 
 
template<typename T> 
void Array<T>::print() 
{ 
  for(int i=0; i<size; ++i) 
    cout << " " << *(ptr+i); 
  cout << endl; 
} 
 
int main() 
{ 
  char a[5] = {'J','a','m','e','s'}; 
  Array<char> charArr(a, 5); 
  charArr.print(); 
 
  int b[5] = { 1, 2, 3, 4, 5}; 
  Array<int> intArr(b, 5); 
  intArr.print(); 
 
  return 0; 
}

A member function of a class template

As with other classes, we can define its member functions either inside or outside the class template. A member function defined outside the class template must begin with the keyword template, followed by a list of class template arguments.


template <typename T> 
return_type class_name<T>::member_name(parm-list) { } 

By default, for an instantiated class template, its member functions are instantiated only when used. If a member function is not used, it is not instantiated.

Class templates and friends

When a class contains a friend declaration, it is independent of whether the class and the friend are templates or not. If a class template contains a non-template friend, the friend is authorized to access all instances of the template. If the friend is itself a template, the class can authorize all instances of the friend template, or only specific instances.


//Predeclaration, used when declaring a particular instance of a template as a friend
template<typename T> class Pal; 
 
//Ordinary class
class C { 
  friend class Pal<C>; //A Pal instantiated with class C is a friend of C
  template<typename T> friend class Pal2; //All instances of Pal2 are friends of C; No predeclaration
}; 
 
//Template class
template<typename T> class C2 { 
  //Each instance of C2 will be declared as a friend with the same type of instantiated Pal, a one-to-one relationship
  friend class Pal<T>; 
  //All instances of Pal2 are friends of each instance of C2 and do not need to be predeclared
  template<typename X> friend class Pal2;  
  //Pal3 is a normal non-template class that is a friend of all instances of C2
  friend class Pal3; 
}; 

A static member of a class template

Class templates can declare static members. Each instance of a class template has its own static member object, and for a given type X, all class_names < X > Objects of type share the same static member instance.


template<typename T> 
class Foo { 
public: 
  void print(); 
  //. Other operating
private: 
  static int i; 
}; 
 
template<typename T> 
void Foo<T>::print() 
{ 
  cout << ++i << endl; 
} 
 
template<typename T> 
int Foo<T>::i = 10; //Initialize to 10
 
int main() 
{ 
  Foo<int> f1; 
  Foo<int> f2; 
  Foo<float> f3; 
  f1.print();  //The output of 11
  f2.print();  //The output of 12
  f3.print();  //The output of 11
  return 0; 
}

We can access a static object of a class template through a class type object, or we can access static members directly using the scope operator (::). Like other member functions of the template class, a static member function is instantiated only when used.


Related articles: