- Specify 44 independent parameter code for separation templates
- Article 45 using the member function template to accept all compatible types
- Templates and generics programming
Clause 44: Extracting code unrelated to templates
Templates can save time and avoid code repetition. For similar classes or functions. Be able to write a class template or function template and let the compiler do the rest. Doing so can sometimes lead to code bloat: the binary code with repeated (or almost repeated) codes, data. or both.
But this time the source code may look very neat.
First, learn a noun: commonality and degeneration analysis (commonality and variability analyses).
Easier to understand. For example, you're writing several functions that use code that works the same way. At this point you tend to move the same code into a new function. To several other function calls. Similarly, suppose you write a class. Some of these parts are the same as the other classes, and you won't be able to write these same parts over and over again, simply by moving the common part into the new class, and using inheritance or compounding (* * clause **32,38,39) to get the original classes to use these common features, The classes of the original (variant part) remains in its original position.
When writing templates, do the same analysis to avoid repetition. The Non-template code is quite clear: you can see that there are two functions or classes between the two iterations. In the template code, however, the repetition is ambiguous. Because there is only one copy of the template source code.
Like what. You're going to write a template for a square matrix with a fixed size that has a function that supports inverse matrix operations.
template<typenamestd::size_t n>//T为数据类型。n为矩阵大小 class SquareMatrix{ public: …… void invert();//求逆运算 }; SquareMatrix<double,5> sm1; sm1.invert();//调用SquareMatrix<double,5>::invert SquareMatrix<double,10> sm2; sm2.invert();//调用SquareMatrix<double,10>::invert
Two copies of invert are detailed above.
These two functions are almost identical (except for an operation 5*5 matrix. An operation 10*10). This is a typical example of code bloat.
The above two functions except the operation matrix size are different. The other same. It is possible to create a function with numeric values instead of repeating the code. So there was the first change to Squarematrix:
Template<TypeNameT>classsquarematrixbase{protected:voidInvertSTD:: size_t matrixsize); ...... };Template<TypeNameTSTD:: size_t n>classSquarematrix:Privatesquarematrixbase<t>{Private:usingSquarematrixbase<t>::invert ();//code obscure base invert,** clause **33 Public: ......voidInvert ()//Beg inverse{ This->invsert (n);//Explain later why this is used} };
Squarematrixbase::invert is simply trying to avoid derived classes code repeatedly, so it replaces public with protected. This function uses THIS->, because the function names within the templated base class are masked by derived classes ( clause **43).
Note that the inheritance relationship between Squarematrixbase and Squarematrix is private. This shows that the base class is intended to help derived classes implementations, which are not **is-a relationships.
Now another question, where is the data for the Squarematrixbase::invert operation? It is in the number of parameters until the matrix size, but the matrix data derived class is known. How do I contact derived class and base class? One approach is to be able to add a number of parameters (such as a pointer) to Squarematrixbase::invert.
This can work, but consider other factors (for example, there are other functions within the squarematrixbase. To manipulate the data), you can add this pointer to the Squarematrixbase class.
template<typename T> class SquareMatrixBase{ protected: SquareMatirxBase(std::size_t n,T* pMem) :sizepData(pMem){} void setDataPtr(T* ptr) {pData=ptr;} …… private: std::size_t size; T* pData; }; template<typename T, std::size_t n> class SquareMatrix:private SquareMatrixBase<T>{ public: SquareMatrix() :SquareMatrixBase<T>(n, data){} …… private: T data[n*n]; };
Objects of this type do not need to allocate memory dynamically. But the object itself can be very large. Another approach is to put the matrix data into the heap
Template<typename T , Span class= "Hljs-symbol" >STD: :size_t n> class squarematrix : private squarematrixbase < t >{ public: squarematrix () Span class= "Hljs-symbol" >:SQUAREMATRIXBASE<T> (n, 0 ), PData (new t [N*n]) {this->setdataptr (Pdata.get ());} ...... private: boost: :SCOPED_ARRAY<T> pData; };
So since. The same type of derived classes will share the base class. Like what. Squarematrix
Article 45: Use member function templates to accept all compatible types
In the template, classes that detail the template's parameters do not derive from the detail type. Take a look at a sample of pointers. The true pointer supports implicit conversions (implitic conversions). The derived class pointer can be implicitly converted to a base class pointer, and a pointer to a Non-const object can be converted to a pointer to a const object, and so on. Like what:
Top{……}; Top{……}; Bottom:public Middle{……}; Top* pt1=new Middle;//Middle* 转换为Top* TopBottom;//Bottom* 转换为Top* Top* pct2=pt1;//TopTop*
If you use a template to define a smart pointer, the conversion above is a bit cumbersome.
template<typename T> class SmartPrt{ public: explicit SmartPtr(T* realPtr); …… }; SmartPtr<Top> pt1=SmartPtr<Middle>(new Middle);//SmartPtr<Middle>转换为SmartPtr<Top> SmartPrt<Top> pt2=SmartPrt<Bottom>(new Bottom); SmartPrt<const Top> pct2=pt1;
There is no relationship between the different details of the same template, even though there are inheritance and derivation relationships between the two types that are detailed.
The compiler treats smartptr and smartptr as completely different types of classes. In order for the above code to compile through, to obtain the conversion between SMARTPTR classes, it must be understood to write them out.
Templates and generics programming
To implement the conversion, it is possible to complete the constructor of the smart pointer, but assuming that the derived class continues to derive, then the constructor joins again, which is obviously unreasonable. Therefore, what we need is not simply to write a constructor for Smartptr, but to compose a construction template. Such a template is called the member function template (abbreviated member templates). function is generated for class
template<typename T> class SmartPrt{ public: template<typename U> SmartPtr(const SmartPrt<U>& other);//member template,为了生成copy cotr …… };
The above code means. For whatever type T and whatever type U, you can generate a smartptr based on SMARTPRT. Copy COTR is not declared as explicit. Because the conversion may be implicit.
This generalization constructor, written for Smartptr, provides much more than we need. We want to create a smartprt based on a smartptr. But do not want to create a smartptr based on a smartptr. Because it is contradictory to public inheritance.
The above code is not complete and the copy COTR is not implemented in Smartptr. Suppose smartptr like Auto_ptr and Tr1::shared_ptr. Provides a GET member function. Returns a smart pointer object, so you can constrain the transformation behavior in the construction template
template<typaname T> class SmartPtr{ public: template<typename U> SmartPrt(const SmartPrt<U>& other) :heldPrt(other.get()){}; getconst{return heldPrt;} …… private: T* heldPrt; };
In the preceding code, there is an implicit conversion: convert U * to t*, which limits the conversion behavior.
member function templates function is more than just a constructor. Another important function is to support assignment operations. For example, TR1 's shared_ptr supports all the construction behavior from compatible built-in pointers, Tr1::shared_ptrs, Auto_ptrs, and Tr1::weak_ptrs, and assignment operations from the above (except Tr1::weak_ptr). Take a look at an excerpt from the TR1 specification about tr1::shared_ptr.
Template<classT>class shared_ptr{ Public:Template<classY>Explicit shared_ptr(y* p);Template<classY>shared_ptr(shared_ptr<Y>Const& R);Template<classY>Explicit shared_ptr(weak_ptr<y>Const& R);Template<classY>Explicit shared_ptr(auto_ptr<Y>Const& R);Template<classY>shared_ptr&operator=(shared_ptr<Y>Const& R);Template<classY>shared_ptr&operator=(auto_ptr<Y>Const& R); ...... };
In addition to the generalization copy constructor, the other constructors are explicit, which means that the implicit conversion of the shared_ptr type is agreed, but the implicit conversion from other smart pointers to shared_ptr is not agreed.
member function templates does not change the basic language rules, and the compiler produces a copy constructor and copy assignment does not conflict.
Tr1:shared_ptr declares a generalized copy constructor, assuming T and Y are the same, the generalized copy constructor is detailed into the normal copy constructor.
The compiler will secretly generate a copy constructor for Tr1::shared_ptr? Or when the Tr1::shared_ptr object expands the construction behavior based on another Tr1::shared_ptr object of the same type, the compiler details the generalized copy constructor template?
Member templates did not change the language rules. Suppose the program needs a copy constructor. You do not declare it, and the compiler will generate it for you. Declaring a generalization copy constructor within class does not prevent the compiler from generating their own copy constructor (Non-template).
If you want to control every aspect of the copy constructor, declare the normal copy constructor. The same rules apply to assignment assignment operations.
Summary
-Use member function templates (member function template) to generate "acceptable all compatible types" functions.
-Assume that the declaration member templates is used to generalize the copy constructor or generalization assignment operation, or to declare a normal copy constructor and copy assignment operator.
Copyright notice: This article blog original articles, blogs, without consent, may not be reproduced.
Effective C + +: Regulation 44-provision of the terms