Function Template)
Templates allow us to generate common functions that can accept parameters of any data type and return values of any type, function Overloading is not required for all possible data types. To some extent, this achieves the role of a macro. Their prototype can be either of the following two types:
template <class identifier> function_declaration;
template <typename identifier> function_declaration;
The difference between the two prototype definitions lies in the use of the keyword class or typename. They are actually exactly equivalent, because the two expressions have the same meaning and execution.
For example, to generate a template and return a larger one of the two objects, we can write as follows:
Template <class generictype>
Generictype getmax (generictype A, generictype B) {return (A> B? A: B );}
In the first line Declaration, we have generated a general data type template called generictype. Therefore, in the subsequent functions, generictype becomes a valid data type. It is used to define two parameters A and B and is used as the return value type of the function getmax.
Generictype still does not represent any specific data type. When the function getmax is called, we can use any valid data type to call it. This data type will be used as pattern to replace where generictype appears in the function. The method for calling a template with a pattern type is as follows:
function <type> (parameters);
For example, to call getmax to compare two int-type integers, you can write as follows:
int x,y;
GetMax <int> (x,y);
Therefore, the call to getmax is like that all generictypes are replaced by Int.
Here is an example:
// Function Template # Include <iostream. h> Template <class T> T getmax (t a, t B ){ T result; Result = (A> B )? A: B; Return (result ); } Int main (){ Int I = 5, j = 6, K; Long L = 10, M = 5, N; K = getmax (I, j ); N = getmax (L, M ); Cout <k <Endl; Cout <n <Endl; Return 0; } |
6 10 |
(In this example, we name the common data type T rather than the generictype, because T is shorter, and it is one of the more common labels of the template, you can use any valid identifier .)
In the above example, we use two parameter types for the same function getmax (): int and long, but only write the implementation of one function, that is to say, we have written a function template and used two different pattern to call it.
As you can see, in our template function getmax (), type T can be used to declare new objects.
T result;
Result is a T-type object, just like a and B, that is, they are all of the same type. This type is the type written in angle brackets when we call the template function <>.
In this specific example, the general type T is used as the parameter of the function getmax. the compiler can also automatically detect the input data type without the need to describe <int> or <long>, therefore, we can write this example as follows:
int i,j;
GetMax (i,j);
Because both I and J are of the int type, the compiler automatically assumes that we want the function to be called according to the int type. This hint method is more useful and produces the same results:
// Function template II # Include <iostream. h> Template <class T> T getmax (t a, t B ){ Return (A> B? A: B ); } Int main (){ Int I = 5, j = 6, K; Long L = 10, M = 5, N; K = getmax (I, j ); N = getmax (L, M ); Cout <k <Endl; Cout <n <Endl; Return 0; } |
6 10 |
Note how to call the template function getmax () in main () in this example without specifying the specific data type in brackets <>. The compiler automatically determines the data type required for each call.
Because our template function only includes one data type (class T) and its two parameters are of the same type, we cannot use two parameters of different types to call it:
Int I;
Long L;
K = getmax (I, L );
The above call is wrong, because our function is waiting for two parameters of the same type.
We can also make the template function accept two or more types of data, for example:
Template <class T>
T getmin (t a, u B) {return (A <B? A: B );}
In this example, our template function getmin () accepts two different types of parameters and returns an object of the same type as the first parameter. In this definition, we can call this function as follows:
Int I, J;
Long L;
I = getmin <int, long> (J, L );
Or, it is easy to use
i = GetMin (j,l);
Although J and l are different types.
Class templates)
We can also define a class template so that a class can have members of a common type, without defining a specific data type when the class is generated. For example:
Template <class T>
Class pair {
T values [2];
Public:
Pair (t first, t second ){
Values [0] = first;
Values [1] = second;
}
};
The class we defined above can be used to store two elements of any type. For example, if we want to define an object of this class to store two integer data types: 115 and 36, we can write as follows:
pair<int> myobject (115, 36);
We can also use this class to generate another object to store any other types of data, for example:
pair<float> myfloats (3.0, 2.18);
In the preceding example, the only member function of the class has been defined by inline. If we want to define a member function outside the class, we must add a template before each function <...>.
// Class templates # Include <iostream. h> Template <class T> class pair { T value1, value2; Public: Pair (t first, t second ){ Value1 = first; Value2 = second; } T getmax (); }; Template <class T> T pair: getmax (){ T retval; Retval = value1> value2? Value1: value2; Return retval; } Int main (){ Pair myobject (100, 75 ); Cout <myobject. getmax (); Return 0; } |
100 |
Note how the getmax function is defined:
template <class T>
T pair::getmax ()
All writing T is required. Every time you define a member function of the template class, you must follow a similar format (here the second t represents the type of the function return value, this may change as needed ).
Template Specialization)
The template is special when the pattern in the template has a specific type, the template has a specific implementation. For example, assume that our class template pair contains a module operation function, and we want this function to be only used when the data stored in the object is an integer (INT) in other cases, we need this function to always return 0. This can be achieved through the following code:
// Template Specialization # Include <iostream. h> Template <class T> class pair { T value1, value2; Public: Pair (t first, t second ){ Value1 = first; Value2 = second; } T module () {return 0 ;} }; Template <> Class pair <int> { Int value1, value2; Public: Pair (INT first, int second ){ Value1 = first; Value2 = second; } Int module (); }; Template <> Int pair <int >:: module (){ Return value1 % value2; } Int main (){ Pair <int> myints (100,75 ); Pair <float> myfloats (100.0, 75.0 ); Cout <myints. Module () <'\ n '; Cout <myfloats. Module () <'\ n '; Return 0; } |
25 0 |
As you can see from the code above, the template is specially defined by the following format:
template <> class class_name <type>
This specialization is also part of the template definition. Therefore, we must write the template <> at the beginning of the definition. And because it is indeed a special definition of a specific type, general data types cannot be used here, so the first pair of angle brackets must be empty. After the class name, we must write the specific data types used in this special feature in angle brackets.
When we specialize in a data type of the template, we must also redefine the special implementation of all the members of the class (if you take a closer look at the above example, we will find that we have to include its own constructor in the specialized definition, although it is the same as the constructor in the general template ). The reason for doing so is that specialization does not inherit any member of the general template.
Parameter Value of the template (parameter values for templates)
In addition to the common type represented by the keyword class or typename prior to the template parameter, the function template and the class template can contain other parameters that do not represent a type, for example, representing a constant, these are usually basic data types. For example, the following example defines a class template for storing Arrays:
// Array template # Include <iostream. h> Template <class T, int n> Class Array { T memblock [N]; Public: Void setmember (INT x, t value ); T getmember (int x ); }; Template <class T, int n> Void array <t, n >:: setmember (INT x, t value ){ Memblock [x] = value; } Template <class T, int n> T array <t, n >:: getmember (int x ){ Return memblock [x]; } Int main (){ Array <int, 5> myints; Array <float, 5> myfloats; Myints. setmember (0,100 ); Myfloats. setmember (3, 3.1416 ); Cout <myints. getmember (0) <'\ n '; Cout <myfloats. getmember (3) <'\ n '; Return 0; } |
100 3.1416 |
You can also set the default value for template parameters, just like setting the default value for function parameters.
The following are examples of template definitions:
Template <class T> // a class parameter.
Template <class T, Class U> // two class parameters.
Template <class T, int n> // a class and an integer.
Template <class T = char> // has a default value.
Template <int tfunc (INT)> // The parameter is a function.
Templates and multi-file Projects)
From the compiler perspective, templates are different from general functions or classes. They are compiled (compiled on demand) only when needed, that is, the code of a template is compiled only when an object needs to be generated (instantiation. When instantiation is required, the compiler generates a special function for a specific call data type based on the template.
When the project becomes larger, the program code is usually divided into multiple source program files. In this case, interfaces and implementations are usually separated. Using a function library as an example, an interface usually includes the prototype definitions of all functions that can be called. They are usually defined in header files with the. h extension, while implementations (function definitions) are in independent C ++ code files.
The macro-like function of the template imposes certain restrictions on multi-file projects: the implementation (Definition) of the function or class template must be in the same file as the prototype declaration. That is to say, we can no longer store interfaces (interfaces) in separate header files, but must store interfaces and implementations in the same file using the template.
Return to the function library example. If we want to create a function template library, we can no longer use header files (. h). Instead, we should generate a template file to store the interface and Implementation of the function template in this file (this file has no common extension, except for not using it. h extension or do not add any extension ). When a project contains templates with simultaneous declarations and implementations multiple times, no link error (linkage errors) is generated because they are compiled only when needed, the compiler compatible with the template should have considered this situation and will not generate duplicate code.