1. Generics and templates
In C + +, the standard template class library, STL, which supports generic applications, is an important part of the C + + standard library that provides users with a common class template and function template for C + + generic design and uses them to support C + + generic design. It can be said that the core technology that supports C + + generics is the template.
1.1 Function templates
Template < TypeName T, TypeName R, TypeName S>t Maxt (R x, S y) { return (x > y)? x:y;}
Take the Maxt template and its function instance as an example, whose invocation format is:
int x;double y, z;...maxt< int, double, double> (x, y, z);
The compiler generates entity functions from the template based on the actual call.
Class 1.2 Templates
Template <typename t>class circle{private: t radius;public: Circle (t r); T area ();};
Class-Template Implementation class member functions:
Template<typename t>circle<t>::circle (T r) { Radius = r;} template<typename t>t Circle<t>: : Area () { return PI * radius * RADIUS;}
Its invocation format:
circle< int >circle_1 (ten); Cout<<circle_1.area () <<endl; circle< double >circle_2 (12.786); Cout<<circle_2.area () <<endl;
STL's template programming is not interested in object-oriented technology, it thinks that the over-encapsulation of the data has an effect on the execution efficiency of the program, and the reason why the template programming of STL has a lot of use of the class template is that the form of the class can be divided into the program code, which makes the code easier to read and manage. Therefore, the large number of STL use is not access to the struct-made class template.
Template<typename t>struct circle{ T Radius; Ciecle (t R;) T area ();};
1.3 Auto keyword
The Auto keyword is set by the automatic derivation data type, which can automatically derive the data type of a variable or object based on the initialization data of the variable when defining a variable.
Template <typename t,typename u>void Multiply (T T, u u) { Auto V = T * U; cout<< "v =" <<V<<ENDL;}
1.4-Decltype Expression
The problem with variable types is difficult to determine primarily in the return value of a function, so the Auto keyword is often present at the position of the function's return value type.
The new standard allows programmers to guide the derivation of the type of function return value by using the Decltype expression.
Template <typename T, TypeName U>auto Multiply (T T, u u), Decltype (t * u) { return T * u;}
2. template parameter 2.1 type parameter
Parameters declared with the keyword typename are of type parameters.
2.2 Non-type parameters
Template <typename T, int b>
The second parameter, B, is a non-type parameter, because the argument can only be an integer variable or an integer number, and it cannot be a data type.
It is important to note that because template parameters are passed and compiled during precompilation, this non-type parameter is constant within the template code and cannot be modified; for this parameter, the current C + + standard only supports integer int, enum, pointer, and reference type.
2.3 Template-Defined parameters
As an example:
Defining a single-parameter class template
Template <typename t> struct s_tmp{ T A; void ply ();};
Defines a two-parameter class template, Templates <typename T, typename r>struct d_tmp{ void ply ();};
Defines a class template that is defined as a parameter in a single-parameter class template templates <template<typename s>class t>struct mytest{ T <int> test; void ply () {test.ply ();}};/ /main function int main () { //Use single parameter class template s_tmp to do parameter instantiation template Mytest Mytest < s_tmp > tt1; Tt1.ply (); Using the two-parameter class template d_tmp to do the parameter instantiation template Mytest Mytest < d_tmp > tt2; Because the template parameter number does not match, the compiler error tt2.ply (); return 0;}
2.4 Implicit provision of function template arguments
Template <yupename t>t Add (t x, t y) { return x +y;}
The normal invocation format is:
Add < int > (45, 66);
The implicit format is:
Add (45, 66);
If the compiler cannot infer the return type accurately, use the previously mentioned keyword auto and decltype.
2.5 Pointer Real number
Template <typename T1, typename t2>t2 Add (T1 A, T2 b) { return B;} int main () { int a = 4; Double b = 6.7; int * PA = & A; Double * PB = & B; cout<< * Add (PA, pb) <<endl; return 0;}
2.6 Modifier const and use of &
Template <typename T1, typename t2>const t2& Add (const t1& A, const t2& b) { return B;} int main () { int a = 4; Double b = 6.7; int * PA = & A; Double * PB = & B; cout<< Add (PA, pb) <<endl; return 0;}
3. Special templates and templates with present rules 3.1 the specificity in the function template
Template <typename t>t Mymax (t t1, T T2) { return t1 > t2? t1:t2;}
The above template obviously does not apply to the string (char *) type, because the string comparison cannot use the notation, and only the comparison function strcmp (T1,T2) of the string can be used.
If the function name of the string comparison function is the same as the Mymax template mentioned above, it is best to include this function in the Mymax template system by declaring it as a template using the keyword template before the function.
Template <>char * Mymax (char * t1, char * T2) { return (strcmp (T1,T2) < 0)? T2:t1;}
Special and partial localization in class 3.2 templates
Normal template Templates <typename T1, typename t2>struct test{ void disp () {cout<< "This is a generic class template"}<<endl;};/ /biasing template Templates <typename t2>struct Test < int, t2>{ void disp () {cout<< "This is a biased class template" <<ENDL;} ;//full-Special templates template <>struct Test < int, float>{ void disp () {cout<< "This is a fully-typed class template" <<endl;}};/ /test main function int main () { test<float,char> tt1; Defines a Normal template object tt1.disp (); Test<int,double> tt2; Defines a biased template object tt2.disp (); Test<int,flost> tt3; Define a fully-specific template object Tt3.disp (); return 0;}
3.3 Template with the present 4. Rvalue reference vs. template 4.1 rvalue Reference
The lvalue can appear to the left of the assignment operator because the expression represents a piece of storage space and can receive and save data. Formally, it must have a variable name that represents this storage space and its data, and the program can obtain an address from its variable name and access the data with that address. Of course, the use of variable names or addresses can also represent data, so an lvalue can also appear to the right of the assignment operator.
The right value can represent data only. An rvalue expression is either the data itself or an expression that can produce a result, although it also occupies a certain amount of storage space, but because it does not have a name and cannot extract the address of the space from its expression, the expression can only appear to the right of the assignment operator. And it can only represent the temporary object with the same life period as the statement, and the temporary object will be destroyed immediately when the program statement is finished, no longer exists.
In summary, the lvalue is an expression with a name and fixed address, while the right value is an anonymous, non-fixed-address object.
int a = ten; int & B = A; For Lvalue a name alias Bconst int & c = A; For Lvalue A, name the constant alias Cconst int & d = +; For the right value 100, name the constant alias dint x = n, y =; const int & i = x + y; For rvalue X+y named constant alias II + = ten; illegal int & F =10; Illegal, the right value can only be referenced frequently
From the above example, we can see that the lvalue can define two kinds of references:
T & alias =lvalue;
Const T & alias =lvalue;
The right value has only one common reference:
Const T & alias =rvalue;
To make the best use of temporary objects, the C + + standard introduces a new type of data---a very reference to rvalue, referred to as an rvalue reference, in the following format:
T && alias =rvalue;
4.2 rvalue Reference App-transfer semantics
An rvalue reference is a new type of data, which means that a function can be implemented by using the function overloading technique. Therefore, the first application of an rvalue reference is the copy constructor and assignment operator overloads of the class. Because this function, overloaded with rvalue references, has resource control transfer, this function is called a transfer semantics.
"Deep copy and Shallow copy"
The problem with deep and shallow copies is related to the resources that the class object controls. The memory space allocated by new is called a resource controlled by the object. Because this space is sparse, after use, the object should release it for use by other program entities, which is called a resource. To accomplish a task, the program often needs to provision the above object resources, so any class that contains resources will define the corresponding copy constructors and destructors.
Class Foo{public: Foo (int x) //constructor { p = new int (x); P points to a space allocated in the heap } Foo (const foo& R) //Deep copy constructor { p = new int; Request space for new object resource * p = * (R.P); //Copy pointer-pointing data to new object pointer to space } ~ Foo () { if (P! = NULL) { delete p; Release the space that the pointer points to } } void Show () { cout<<p<< "" <<*p<<endl; } Private: int * p; The pointer that holds the resource space address};int main () { Foo foo1 (); Foo Foo2 (foo1); Foo1.show (); Foo2.show (); return 0;}
Given above is an example of a deep copy constructor, given below is an example of a shallow copy constructor function.
Foo (const Foo & R) //Shallow copy constructor { p = r.p; Simple copy makes P and R.P point to the same space}
As you can see, a shallow copy does not need to allocate space for a resource in the target object or to assign a large amount of resource data between the two objects, so it is fast and saves memory. If there is a way to set the source object pointer to null, then the result is that the source object to the control of the resources to the target object, with the c++11 is to achieve the "transfer" of resource control rights. Obviously, this practice is particularly appropriate for situations where the source object is a temporary object (the right value).
Because the function creates a temporary object during the process of returning values by way of a value, it is necessary to design this copy constructor with "Transfer semantics" for the class.
Copy constructors with transfer semantics are as follows: (Transfer semantic copy constructor is actually rvalue reference copy constructor)
Foo (foo && r) { p = r.p; A simple copy causes P and R.P to point to the same space R.P = null; Set R.P to null pointer, source object discards resource control}
Given the benefits of rvalue referencing, the assignment operator with the transfer semantics becomes the proper part of the class design effort. The following is a two assignment operator overload code:
Foo 7 operator = (const Foo & R) //Deep copy assignment operator overload { Delete p; Release the original resource p = new int; The pointer points to the new memory space requested for the resource * p = * (R.P); Source to target assignment resource data return * this; Returns the target object}foo & oprator = (Foo && r) //Transfer semantics assignment operator overload { Delete p; Release the original resource p = R.P; Source and target object shared resource R.P = null; Source object discards resource control return * this; Return target Object}
4.3 rvalue reference App-transfer function Move ()
Seeing the benefits of rvalue referencing, the Lvalue also wanted to share the benefits, so c++11 introduced the move () function, which was prototyped as follows:
T && Move (T & Val); It takes a parameter Val and then returns an rvalue reference to this parameter
Foo2 = Move (FOO1); The Foo1 object invokes the transfer semantics operator overload in the class to implement the assignment operation.
If the move () function is used properly, its effect is huge, the data exchange function Std::swap () in the STL library uses this function, and its schematic code is as follows:
void Swap (T & A, T & B) { t tmp = Move (a); A = Move (b); b = Move (TMP);}
If T is a class type, it contains a lot of resources, so it is not necessary to transfer the semantics of the technology to do this exchange of resources, and here only exchange 3 times the pointer to complete the task, the efficiency is very high.
4.4 rvalue Reference App-parameters perfect forwarding template 4.4.1 The problem of perfect forwarding and its solution
Before c++11, the parameter perfect forwarding problem of the template has not been solved very well, mainly because the Rvalue parameter is forwarded to the left value by the template, so that the function that can use the Rvalue parameter cannot be called within the template.
void Func (int v) { cout<< "call succeeded" <<ENDL;} Forwarding templates template <typename t>void Tmp (T a) { Func (a);} Test program int Main () { int x = 1; TMP (ten); Right-value parameter Tmp (x); Left value passed return 0;}
As can be seen from the test results, the template can not only implement the right-value parameter forwarding, but also to implement the Lvalue parameter forwarding, but according to the requirements of the whole is not perfect, because it is a value transfer, it needs to create temporary objects during the forwarding process and copy the data, resulting in additional consumption.
In order to not use temporary objects that are consumed during the forwarding process, it means that the parameters of the forward template must be reference types.
void Tmp (T & A) { Func (a);}
TMP (x), at which point the value of the left value is passed
However, it is not a perfect forwarding because it cannot be forwarded with rvalue parameters. The template parameter type that is capable of perfect forwarding must be an rvalue reference.
void Func (int & V) { cout<< "call succeeded" <<ENDL;} Forwarding templates template <typename t>void Tmp (T && a) { Func (a);} Test program int Main () { int x = 1; TMP (ten); Right-value parameter Tmp (x); Lvalue reference passed return 0;}
C++11 develops the forward () function to convert the Rvalue parameter transformed by the template to an lvalue and then back again.
Derivation rules for 4.4.2 template parameter types-reference folding rules
The following table is: The collapse rule for the reference (reference collapsing rule)
Serial number |
argument types (in int, for example) |
Template parameter types |
The result of type derivation in the form of real union |
1 |
int & |
T & |
int & |
2 |
int && |
T & |
int & |
3 |
int & |
T && |
int & |
4 |
int && |
T && |
int && |
The following table is: Two special rules for template (T && type) parameter derivation
Serial number |
Actual parameter type |
Type derivation Results |
1 |
Left value (lvalue) |
T & |
2 |
Right value (rvalue) |
T |
Write a test code:
Lvalue reference type parameter target function void Func (int & V) { cout<< "& Call succeeded" <<ENDL;} Rvalue reference type parameter target function void Func (int && v) { cout<< "&& call succeeded" <<ENDL;} Forwarding templates template <typename t>void Tmp (T && a) { Func (a);} Test program int Main () { int x = 1; int & y = x; TMP (x); The arguments are lvalue Tmp (y); The argument is an lvalue reference Tmp (+); The argument is the right value of return 0;}
The test results are as follows:
& Call succeeded & Call succeeded & Call succeeded
As you can see from the results, when the template finds that the parameter x is an lvalue reference of int, the template parameter T is deduced as the int& type, followed by the two references after the template parameter definition T & connected, the type is int&&& Finally, the compiler simplifies the parameter type to the int& type according to the collapse rule, which is the desired type of the target function.
The expected function is not correctly called when using the right value (100) as an argument, because the template converts the rvalue to an lvalue. The correct approach should be as follows:
If (argument is rvalue) func (Move (a)), Else func (a);
4.4.3 guaranteed-forward () function template for correct forwarding of parameter types
Move () function, can only convert the t& type to t&& type, so in order to avoid the use of if-else structure, c++11 with the original data type cast function template static_cast, so the code to invoke the target function is as follows:
Template <typename t>void Tmp (t && a) { Func (static_cast< t &&> (a));}
Since the template parameter of static_cast is identical to the tmp template parameter type, it is t&&, so the input type of the parameter is exactly the same as the type of the function's real receiving parameter. Come in is the right value, send to the function must also be the right value (this is the result of the static_cast conversion);
In fact, static_cast is only useful when the parameter is the right value. To differentiate Move () and static_cast, and make it more semantic, c++11 encapsulates static_cast as a function template Std::forward. The correct code is as follows:
Template <typename t>void Tmp (t && a) { Func ( forward< T > (a));}
C + + Generic technology fundamentals-templates