Item 45: Use member function templates (member function template) to accept "all compatible types" ("all compatible types ")
By Scott Meyers
Translator: fatalerror99 (itepub's nirvana)
Release: http://blog.csdn.net/fatalerror99/
Smart pointers(Smart pointer) is a objects that acts like a pointer but adds functionality not provided by the pointer. For example, item 13 describes how the standard auto_ptr and tr1: shared_ptr are used to automatically delete heap-based resources (heap-based resources) at the right time. Iterators (iterators) in STL containers are almost always smart pointers (smart pointers); you absolutely cannot expect to use "++" to put a built-in Pointer (built-in pointer) you can move a node from a linked list (Linear Linked List) to the next node, but list: iterators can do this.
One thing that real pointers does well is support implicit conversions (implicit conversion ). Derived class pointers (derived class pointer) is implicitly converted to base class pointers (base class pointer), pointers to non-const objects (pointer to a very large number of objects) convert to pointers to const objects (pointer to constant object), and so on. For example, consider some conversions in a three-level hierarchy (three-tier inheritance system:
Class top {...};
Class middle: Public top {...};
Class bottom: public middle {...};
Top * pt1 = new middle; // convert middle * => top *
Top * pt2 = new bottom; // convert bottom * => top *
Const top * pct2 = pt1; // convert top * => const top *
Imitating these conversions in user-defined smart pointer classes (User-Defined smart pointer class) requires skill. Let's make the following code compile:
Template <typename T>
Class smartptr {
Public: // smart pointers are typically
Explicit smartptr (T * realptr); // initialized by built-in pointers
...
};
Smartptr <top> pt1 = // convert smartptr <middle> =>
Smartptr <middle> (New Middle); // smartptr <top>
Smartptr <top> pt2 = // convert smartptr <bottom> =>
Smartptr <bottom> (new bottom); // smartptr <top>
Smartptr <const top> pct2 = pt1; // convert smartptr <top >=>
// Smartptr <const top>
There is no inherent relationship between different instantiations of the same template (Template). Therefore, the compiler considers smartptr <middle> and smartptr <top> to be completely different classes, it is not closer to (for example) the relationship between vector <float> and widget. To obtain the desired conversions between smartptr classes, we must explicitly program them.
In the above smart pointer sample code, each statement creates a new smart pointer object (smart pointer object ), so now we focus on how we write smart pointer constructors to run it as we want. A key fact is that we cannot write all the constructors we need ). In the above hierarchy (inheritance system), we can construct a smartptr <top> from a smartptr <middle> or a smartptr <bottom>, but if this hierarchy (Inheritance System) extended, smartptr <top> objects must also be constructed from other smart pointer types (smart pointer type. For example, if we add
Class belowbottom: Public bottom {...};
We need to support creation from smartptr <belowbottom> objects to smartptr <top> objects, and we certainly do not want to change the smartptr template to do this.
In general, the number of constructors we need is infinite. Because a template can be instantiated and countless functions are generated, it seems that we do not need to provide a constructor for smartptr.Function(Constructor), we need a constructorTemplate(Constructor template ). The templates (Template) isMember function templates(Member function template) (often calledMember templates(Member template) -- generate the templates (Template) Example of a class member functions (member function:
Template <typename T>
Class smartptr {
Public:
Template <typename u>// Member Template
Smartptr (const smartptr <u> & other );// For a "Generalized
... // Copy constructor"
};
This means that for each type of T and each type of U, you can create a smartptr from a smartptr <u>, because smartptr <t> has a constructor (constructor) that obtains the parameter smartptr <u> ). Constructor (constructor) like this -- creates Constructor (constructor) for another object from different instantiated objects of the same template type (Template) (for example, create a smartptr from a smartptr <u>) -- sometimes calledGeneralized copy constructors(Generic copy constructor ).
The above generalized copy constructor (generic copy constructor) is not declared as explicit. This is intentional. The type conversion between built-in Pointer types (built-in pointer type) (for example, from a derived class pointer to a base class pointer) is implicit and does not require cast (forced transformation, so it is reasonable to let smart pointers imitate this line. The explicit it is omitted in the templatized Constructor (templated constructor) to achieve this.
As a declaration, smartptr's generalized copy constructor (generic copy constructor) provides more things than we want. Yes, we need to be able to create a smartptr from a smartptr <bottom>, but we do not need to be able to create a smartptr from a smartptr <top> <bottom>, this is like reversing the meaning of public inheritance (Public inheritance) (see item 32 ). We do not need to be able to create a smartptr from a smartptr <double>, because this is not commensurate with implicit conversion (implicit conversion) from int * to double. We must try to filter out the group of member functions (member function) generated from this Member template.
If smartptr follows the steps of auto_ptr and tr1: shared_ptr, a built-in Pointer (built-in pointer) held by smart pointer is provided) (See item 15), we can use the constructor template (constructor template) implementation to limit the conversion to the desired range:
Template <typename T>
Class smartptr {
Public:
Template <typename u>
Smartptr (const smartptr <u> & Other) // initialize this held PTR
:Heldptr (other. Get ()){...} // With other's held PTR
T * Get () const {return heldptr ;}
...
PRIVATE:// Built-in Pointer held
T * heldptr;// By the smartptr
};
We use Member initialization list (member initialization list ), use a pointer of the <u> type held by smartptr to initialize the data member (data member) of the smartptr <t> type T ). This can be compiled only when "an implicit conversion (implicit conversion) exists from a u * pointer to a T * Pointer", which is exactly what we want. The final result is that smartptr <t> now has a generalized copy constructor (generic copy constructor) which can be compiled only when a compatible type (compatible type) parameter is passed in.
The purpose of the member function templates (member function template) is not limited to Constructors (constructors ). Another common task is to support assignment (Value assignment ). For example, tr1 shared_ptr (see item 13 again) supports all compatible built-in pointers (built-in pointers), tr1: shared_ptrs, auto_ptrs and tr1 :: weak_ptrs (see item 54) construction, and all these values except tr1: weak_ptrs. Here is an excerpt from the tr1 specification about tr1: shared_ptr, including its preference to use class rather than typename when declaring template parameters (Template parameter. (As described in item 42, their meanings are strictly consistent in the context .)
Template <class T> class shared_ptr {
Public:
Template <class Y> // construct from
Explicit shared_ptr (y * P); // any compatible
Template <class Y> // built-in pointer,
Shared_ptr (shared_ptr <Y> const & R); // shared_ptr,
Template <class Y> // weak_ptr, or
Explicit shared_ptr (weak_ptr <Y> const & R); // auto_ptr
Template <class Y>
Explicit shared_ptr (auto_ptr <Y> & R );
Template <class Y> // assign from
Shared_ptr & operator = (shared_ptr <Y> const & R); // any compatible
Template <class Y> // shared_ptr or
Shared_ptr & operator = (auto_ptr <Y> & R); // auto_ptr
...
};
Except for generalized copy constructor (generic copy constructor), all these constructors are explicit. This means that from one shared_ptr type to another implicit conversion (implicit conversion) is allowed, but from a built-in Pointer (built-in pointer) or its smart pointer type (smart pointer type)ImplicitConversion (implicit conversion) is not permitted. (ExplicitConversion (explicit conversion)-for example, through a cast (forced transformation)-still works .) It is also important to note that the Constructors (constructors) and assignment operators (Value assignment operators) Methods transmitted by auto_ptrs to tr1: shared_ptr are not declared as Const. In contrast to tr1:: shared_ptrs and tr1: weak_ptrs are transmitted. This is an inevitable result of the fact that auto_ptrs needs to be unique and changed when being copied (see item 13 ).
Member function templates (member function template) is an excellent thing, but they do not change the basic rules of this language. The compiler described in item 5 can generate four member functions (member functions), two of which are copy constructor (copy constructor) and copy assignment operator (copy assignment operator ). Tr1: shared_ptr declares a generalized copy constructor (generic copy constructor). Obviously, when the types T and Y are the same, generalized copy constructor (generic copy constructor) can be instantiated to become "normal" copy constructor ("regular" copy constructor ). Then, when a tr1: shared_ptr object is constructed from another tr1: shared_ptr object of the same type, the compiler generates a copy constructor (copy constructor) for tr1: shared_ptr ), or is it possible to instantiate generalized copy constructor template (generic copy constructor template )?
As I said, member templates (member templates) does not change the language rules, and the rules stipulate that if a copy constructor (copy constructor) is required and you do not declare it, it will automatically generate one for you. Declaring a generalized copy constructor (generic copy constructor) (a member template) in a class does not prevent the compiler from generating their own copy constructor (copy constructor) (Non-template), so if you want to fully control copy construction, you must declare a generalized copy constructor (generic copy constructor) declare another "normal" copy constructor ("regular" copy constructor ). This also applies to assignment (assign value ). This is an excerpt from the definition of tr1: shared_ptr, which can be used as an example:
Template <class T> class shared_ptr {
Public:
Shared_ptr (shared_ptr const & R );// Copy constructor
Template <class Y>// Generalized
Shared_ptr (shared_ptr <Y> const & R );// Copy constructor
Shared_ptr & operator = (shared_ptr const & R );// Copy assignment
Template <class Y>// Generalized
Shared_ptr & operator = (shared_ptr <Y> const & R );// Copy assignment
...
};
Things to remember
- Use member function templates (member function template) to generate functions that accept all compatible types.
- If you declare Member templates for generalized copy construction (generic copy construction) or generalized assignment (generic assignment ), you still need to declare normal copy constructor (regular copy constructor) and copy assignment operator (copy assignment operator ).