CUJ: Standard Library: Allocator can do?

Author:    Updated:2008-3-26 12:26:38
Allocator is the C + + language standard for the most mysterious part of. They were rarely explicit use of a standard is not clear at what time they should be used. Today's STL allocator with the original proposal is very different, in this process, there are still two other design - two of which were dependent on the characteristics of language, and until recently only in a very few available on the compiler. Allocator of the functions, standards in some areas seems to additional commitments, and in some other aspects of the withdrawal commitments.

     This column will discuss what you can do allocator, as well as how to define its own version. I will discuss the C + + standard defined by the allocator: the introduction of pre-standard era design, or attempt to bypass the defective compiler, will only increase confusion.

When not in use Allocator
     C + + standard in the Allocator divided into two: a common set of requirements (described in § 20.1.5 (Table 32)), and called std:: allocator of class (described in § 20.4.1). If a class to meet the needs of Table 32, we call it an allocator. Std:: allocator category to meet those needs, it is an allocator. It is standard procedure for only one of the pre-defined allocator category.

     Each C + + programmers are already aware of dynamic memory allocation: new X wrote to allocate memory and the creation of a new type of object X, wrote delete p p referred to the destruction of the targets and returned to their memory. You have reason to believe that allocator will use new and delete - but they did not. (C + + standard will be:: operator new () described as "allocation function," but very strange, allocator is not the case.)

     The allocator of the most important fact is that they are only for one purpose: Packaging STL containers in the memory management on the details of the lower. You should not direct their own code allocator member function calls, unless the process of writing its own STL containers. You should not attempt to use to achieve operator new allocator []; this is not done the allocator. If you are unsure whether to use allocator, it will not be used.

     Allocator is a category, has called allocate () and deallocate () member function (equivalent to malloc and free). It also provides for the maintenance of the memory allocated by the auxiliary function and instructions how to use these memory typedef (pointer type or invoke the name). If a user to use STL containers provided by the allocator it necessary to allocate all memory (pre-defined STL containers can all do so; they have a template parameters, the default value is std:: allocator), and you can by providing their own the allocator to control its memory management.

     Such flexibility is limited: from containers still to decide how much memory it will have to apply for and how to use them. In containers for more memory, you can call it that low-level control function, but you can not use allocator to make the act as a vector as a deque. Even so, sometimes, the limited flexibility would be useful. For example, suppose you have a special fast_allocator can quickly assign and release memory (perhaps through abandoned thread-safe, or use a small local stack), you can write std:: list <T, fast_allocator <T> > rather than simply std:: list <T> standards to allow the use of its list.

     If this seems strange to you, then right. There is no reason for the use of conventional code of the allocator.

Definition of a Allocator
     Allocator on this point you have already seen: they are templates. Allocator, and containers, there are value_type, and the allocator value_type must be matched to the use of its containers value_type. This is sometimes more ugly: value_type map of a complex, so explicit call allocator the map looks like this, std:: map <K, V, fast_allocator <std:: pair <const K, V>>>. (As usual, typedef this will be helpful.)

     To start a simple example. According to the C + + standard, std:: Construction in the allocator:: operator new (). If you are using an automated memory usage tracking tool, std:: allocator will be more easier to do more convenient. We can use malloc () instead:: operator new (), but we do not consider (in the good std:: allocator can be found in the Lab) complex performance optimization measures. We will call this simple malloc_allocator allocator.

     Since malloc_allocator memory management is very simple, we will be able to focus on all the allocator STL shared by the model. First, some types: allocator is a class template, and its example for a certain type of T allocate memory. We provide a sequence of typedef, with a description of how to use this type of object: value_type that T itself, the other is a modification of the target word and quote.

     Template <class T> class malloc_allocator

     (

     Public:

       Typedef T value_type;

       Typedef value_type * pointer;

       Typedef const value_type * const_pointer;

       Typedef value_type & reference;

       Typedef const value_type & const_reference;

       Typedef std:: size_t size_type;

       Typedef std:: ptrdiff_t difference_type;

       ...

     );

     These types of containers and STL is very similar, this is not a coincidence: Items are often directly from the allocator from these types.

     Why are there so many typedef? You may think that pointer is superfluous: it is value_type *. This vast majority of the time is right, but you may sometimes think the definition of non-traditional allocator, and its pointer is a pointer-like the class, or non-manufacturers target specific types value_type __far *; allocator is non-standard extensions the standard hook. Unusual address pointer types also exist () member function of reasons, it is only in malloc_allocator & operator () another way:

     Template <class T> class malloc_allocator

     (

     Public:

       Pointer address (reference x) const (return &x;)

       Const_pointer address (const_reference x) const (

         Return &x;

       )

       ...

     );

     Now we can begin the real work: allocate () and deallocate (). They are simple, but not very much like malloc () and free (). We pass allocate () two parameters: We are assigned for the number of space objects (max_size return to the greatest possible success of the request), as well as an optional address values (can be used as a location tips). Malloc_allocator such as the allocator simple tips that did not use, but designed for high-performance allocator might use it. The return value is a pointer at the memory block, it is enough to accommodate the n value_type type of object and the right alignment.

     We also passed deallocate () two parameters: Of course one is the guide, but also an element of the same values. Vessels must have their own size; pass allocate and deallocate must match the size parameters. Similarly, the additional parameters for efficiency exist, and equally, malloc_allocator not use it.

     Template <class T> class malloc_allocator

     (

     Public:

       Pointer allocate (size_type n, const_pointer = 0) (

         Void * p = std:: malloc (n * sizeof (T));

         If (! P)

           Throw std:: bad_alloc ();

         Return static_cast <pointer> (p);

       )

       Void deallocate (pointer p, size_type) (

         Std:: free (p);

       )

       Size_type max_size () const (

         Return static_cast <size_type> (-1) / sizeof (value_type);

       )

       ...

     );

     Allocate () and deallocate () member function is handled Uninitialized memory, they do not structure and the destruction of objects. Statements a.allocate (1) more like malloc (sizeof (int)) rather than the new int. Allocate from the use of () access memory, you must create this memory objects through deallocate () before the return of memory, you need to destroy those targets.

     C + + provides a mechanism to a specific memory location in the creation of objects: placement new. If you write new (p) T (a, b), then you are called T constructor create a new object, as you write the new T (a, b) or T t (a, b). Difference is that when you write new (p) T (a, b), you specify the location of the object is created: p at the address. (Naturally, p must be at a sufficiently large memory, and it must memory is not in use; you can not build at the same address two different audiences). You can also call out the object structure function without the release of memory, as long as the write p-> ~ T ().

     These features are rarely used, because usually the memory allocation and initialization is carried out in conjunction with: Use of uninitialized memory is inconvenient and dangerous. You need such low-level skills is one of several little you write a container type, so memory allocator will be allocated and initialized decoupling. Members function construct () call placement new, and members of destory function () function call out conformation.

     Template <class T> class malloc_allocator

     (

     Public:

       Void construct (pointer p, const value_type & x) (

         New (p) value_type (x);

       )

       Void destroy (pointer p) (p-> ~ value_type ();)

       ...

     );

     (Why allocator those members of function, when containers can be used directly placement new? One of the reasons is to hide awkward syntax, and the other is to write a more complex if the allocator, you may want to destroy the structure and object construct () and destroy () and some other side effects, for example, all the allocator may maintain a current log of activities object.)

     These members of a function is not static and, therefore, the use of containers in the allocator before the first thing to do is to create an allocator targets - that is to say we should define some constructor function. However, we do not need assignment operators: Once the containers to create its allocator, the allocator to never be changed. Table 32 in the allocator does not include the demand for assignment. Is based on security, in order to ensure that no one accidental use of assignment operators, we will be out this function may be automatically generated.

     Template <class T> class malloc_allocator

     (

     Public:

       Malloc_allocator () ()

       Malloc_allocator (const malloc_allocator &) ()

       ~ Malloc_allocator () ()

     Private:

       Void operator = (const malloc_allocator &);

       ...

     );

     These functions do not actually tectonic do anything because the allocator does not require any initialization of variables. For the same reason, any two malloc_allocator are interchangeable; if a1 and a2 are the type of malloc_allocator <int>, we can freely through a1 to allocate () memory and then passed to deallocate a2 () it. We therefore define a comparison operation to show all the malloc_allocator target is equivalent to:

     Template <class T>

     Inline bool operator == (const malloc_allocator <T> &,

                            Const malloc_allocator <T> &) (

       Return true;

     )

     Template <class T>

     Inline bool operator! = (Const malloc_allocator <T> &,

                            Const malloc_allocator <T> &) (

       Return false;

     )

     You would expect an allocator, it is an object of different replacement? Of course - but it is very difficult to provide a simple and useful examples. A distinct possibility is memory pool. Its large C procedure is common, from several different locations (the "Pool") allocate memory, rather than anything directly use malloc (). This has several advantages, one of which is it only takes a single function call to reclaim all of the memory associated with a particular phase of the program. Use memory pool procedures, such as the definition and mempool_Free mempool_Alloc such tools function, mempol_Alloc (n, p) from the pool in the distribution of p n bytes. Very easy to write a mmepool_alocator to match the structure: Each object is a member of mempool_allocator variable to specify in which it bundled Ikegami, and mempool_allocator:: allocate () will call mempool_Alloc () from the corresponding pool access memory. [Note 1]

     Finally, we have reached the allocator of the definition of a delicate parts: the mapping between the different types. The question is, a allocator category, such as malloc_allocator <int>, all constructed around a single value_type: malloc_allocator <int>:: pointer is int *, malloc_allocator <int> (). Allocate (1) sufficient to accommodate a return of objects int memory, and so on. However, usually, the use of such containers malloc_allocator may have to deal with more than one type. For example, a list of, distribution int target; In fact, it targets the distribution list node. (We will study the details in the next paragraph.) So, when you create a std:: list <int, malloc_allocator <int>>, the list must be dealt with malloc_allocator <int> into list_node types of malloc_allocator.

     This mechanism known as rebinding, it has two parts. First, to set a value_type X1 is the allocator type A1, you must be able to write an allocator type A2, A1 and the same, apart from value_type is X2. Secondly, given the A1 type of object a1, you must be able to create an equivalent A2 type object a2. These two parts are members of the template, which is allocator can not be older compiler support, or the support of the reasons for the poor.

     Template <class T> class malloc_allocator

     (

     Public:

       Template <class U>

       Malloc_allocator (const malloc_allocator <U> &) ()

       Template <class U>

       (Struct rebind typedef malloc_allocator <U> other;);

       ...

     );

     In fact, this means that a allocator category is not merely a single class; it must be family-related, each has its own value_type. An allocator category must have a rebind members, as it allows from one type into another family with a class possible.

     If there is a allocator type A1, corresponding to the type of another value_type is typename A1:: template rebind <X2>:: othere [Note 2]. As you can convert one type to another one, the template structure with the conversion function allows you to switch Value: you can write malloc_allocator <int> (a), irrespective of the type is a malloc_allocator <int> or malloc_allocator <double> or malloc_allocator <string>. As usual, the conversion malloc_allocator constructor function is no need to do anything, because no member malloc_allocator variables.

     Incidentally, although the majority of the allocator a template parameters (allocator of value_type), but this is not the provisions of demand, but so often happened. Rebinding mechanism in the multi-template parameters allocator also work well:

     Template <class T, int flags> class my_allocator

     (

     Public:

       Template <class U>

       (Struct rebind typedef my_allocator <U, flags> other;);

       ...

     );

     Finally, the last details: how the void we do? Sometimes a container must involve the void pointer (once again, we will study the details in the next paragraph), and rebinding mechanism almost to the things we need, but not entirely. It does not work, because we will write similar malloc_allocator <void>:: pointer code, and we use the definition of malloc_allocator example of the void, is illegal. It uses sizeof (T), and involved the T & T is void when, these two are illegal. Finding a solution as simple as the problem itself: the void of special malloc_allocator, throw away all other, leaving only a void we need to target.

     Template <> class malloc_allocator <void>

     (

       Typedef void value_type;

       Typedef void * pointer;

       Typedef const void * const_pointer;

       Template <class U>

       (Struct rebind typedef malloc_allocator <U> other;);

     It is! Complete source malloc_allocator see Listing 1.

Use Allocator
     Allocator use of the most simple method is, of course, pass them as parameters containers; use

     Std:: vector <char, malloc_allocator <char>> V;

Replace the simple std:: vector <char>, or

       Typedef std:: list <int, mempool_allocator <int>> List;

       List L (mempool_allocator <int> (p));

Replace the simple std:: list <int>.

     But you can do more. STL selling point is that it is scalable: As you can write your own allocator, you can also write your own containers category. If you are very careful, and you write containers of the allocator use it to deal with all the associated memory operations, then others will be able to join their own user-defined allocator.

     Such as std:: vector and std:: list of such containers is very complex, and most complex and memory management irrelevant. Let us take a simple example, so we can only focus on the allocator. Consider a fixed-size array of categories, Array, and the number of elements in the constructor function is set, and after this will not change. (This is a bit like std:: valarray a simplified version.) It has two template parameters, type and element types allocator.

     Containers, and the allocator like to affirm start nested types: value_type, reference, const_reference, size_type, difference_type, iterator, and const_iterator. Usually, the vast majority of these types can be directly from the allocator get it - and this explains why the containers must be value_type allocator and the match.

     Of course, the iterator type do not normally come from the allocator; usually iterator is a category, depends entirely on the inner containers said. Array analog containers usually see simple, as it will in fact be all the elements stored in a single continuous block of memory; As long as we maintain memory block at the beginning and end of the two indicators. Iterator is the target.

     Before going further, we have to decide: How will we storage allocator? Constructor function will accept a allocator object as a parameter. We must be in containers throughout the life of a copy of preserving it, because Analysis configuration function also needs it.

     In feeling, no problems here: As long as we affirms that a member Allocator types of variables, and then use it. Is a proper way, but不爽. After all, 99% of the time, users do not want to consider the allocator things they will write Array <int> and use the default value - and the default is an allocator may not members of any non-static variables like air. Even if the problem is Allocator is an empty category, the members will also be variable costs. (This is the C + + standard required.) We Array will be three categories of expenses word, not two. Perhaps a word of the additional overhead is not a big problem, but always不爽, which forced all users to use one almost never assume the function of the overhead.

     Are many ways to solve this problem, some of which used the special traits of the type and partial. Perhaps the simplest solution is to use (private) rather than the succession of variables. Optimizing Compiler be allowed to swap space-based categories, but nowadays the majority of the compiler have done so.

     Eventually, we can write the definition of a skeleton:

     Template <class T, class Allocator = std:: allocator <T>>

     Class Array: private Allocator

     (

     Public:

       Typedef T value_type;

     

       Typedef typename Allocator:: reference reference;

       Typedef typename Allocator:: const_reference

               Const_reference;

       Typedef typename Allocator:: size_type size_type;

       Typedef typename Allocator:: difference_type

               Difference_type;

       Typedef typename Allocator:: pointer iterator;

       Typedef typename Allocator:: const_pointer const_iterator;

       Typedef Allocator allocator_type;

       Allocator_type get_allocator () const (

         Return static_cast <const Allocator&> (* this);

       )

       Iterator begin () (return first;)

       Iterator end () (return last;)

       Const_iterator begin () const (return first;)

       Const_iterator end () const (return last;)

       Array (size_type n = 0,

             Const T & T x = (),

             Const Allocator & a = Allocator ());

       Array (const Array &);

       ~ Array ();

       Array & operator = (const Array &);

     Private:

       Typename Allocator:: pointer first;

       Typename Allocator:: pointer last;

     );

     To meet the demand of containers (The Complete Works of demand seen at the C + + standard § 23.1, Table 65), this is not all we want to model, but the vast majority are completely unrelated and allocator. For our purposes, the most interested members of the constructor function (memory and the creation of its distribution targets), and Analysis of configuration function (memory and the release of its destruction of memory).

     Structure-function initializes allocator category, was sufficient to accommodate n elements of a memory (if we are to write things like vector, we may apply for larger memory used for growth), and then traverse to create early memory of the value of the copy before. The only question is abnormal security: If a certain element of the structure function dished out an anomaly, we must remove all we do.

     Template <class T, class Allocator>

     Array <T, Allocator>:: Array (size_type n,

                                Const T & x,

                                Const Allocator & a)

       : Allocator (a), first (0), last (0)

     (

       If (n! = 0) (

         First Allocator =:: allocate (n);

         Size_type i;

         Try (

           For (i = 0; i <n; + + i) (

             Allocator:: construct (first + i, x);

           )

         )

         Catch (...) (

           For (size_type j = 0; j <i; j + +) (

             Allocator:: destroy (first + j);

           )

           Allocator:: deallocate (first, n);

           Throw;

         )

       )

     )

     (You may ask why we have handwritten this cycle; std:: uninitialized_fill () do not have the things we need? Almost, but not exactly the same and we must call allocator members of the structure and function is not a simple pacement new. Perhaps the future of C + + standard will be included as an acceptable parameters allocator uninitialized_fill () function, and as a result no longer need this explicit cycle.

     Analysis of relatively simple structure function, because we do not need to worry about security anomalies: T Analysis of the structure function is assumed to be abnormal never dished out.

     Template <class T, class Allocator>

     Array <T, Allocator>:: ~ Array ()

     (

       If (first! = Last) (

         For (iterator i = first; i <last; + + i)

           Allocator:: destroy (i);

         Allocator:: deallocate (first, last - first);

       )

     )

     Our simple and does not require the use of an array of bundled or conversion, but this is only because Array <T, Allocator> never produced outside the T type object. When you define a more complex data structure, there have been other types. For example, consider value_type T is the linked list of list. It is usually linked list of nodes, each node contains a T type of object and a point to the next node pointer. Thus, as the first attempt, we may definition linked list of nodes:

     Template <class T>

     Struct List_node

     (

       T val;

       List_node * next;

     );

     To a new list by adding the value of the process may seem like this:

L using a value_type is List_node <T> the allocator, as a new node allocation of memory.

L T is using a value_type the allocator in the node position val constructing a new element.

L nodes will be connected to the appropriate position.

     This process need to deal with two different allocator, one of which is through the rebinding of another obtained. Almost all its procedures are working very well, even if the allocator is for the purpose of the complexity of the procedure. It is unable to complete the allocator to provide some unusual types of indicators. It will obviously depend on the use of List_node <T> * types of common indicators.

     If you extremely ambitious and wanted to be the guideline for other types of transmission allocator, everything suddenly become a lot more complicated - from one node to another node pointer is no longer List_node <T> * * or void, but It must be able to obtain from the allocator of a certain type. Directly is not possible to achieve: an incomplete examples of the type of allocator is illegal and therefore, unless List_node has been fully defined, not talking about pointing List_node indicator. We affirm the need for a compact sequence.

     Template <class T, class Pointer>

     Struct List_node

     (

       T val;

       Pointer next;

     );

     Template <class T, class Alloc>

     Class List: private Alloc

     (

     Private:

       Typedef typename Alloc:: template rebind <void>:: other

               Void_alloc;

       Typedef typename Void_alloc:: pointer Voidptr;

       Typedef typename List_node <T, Voidptr> Node;

       Typedef typename Alloc:: template rebind <Node>:: other

               Node_alloc;

       Typedef typename Node_alloc:: pointer Nodeptr;

       Typedef typename Alloc:: template rebind <Voidptr>:: other

               Voidptr_alloc;

       Nodeptr new_node (const T & x, Nodeptr next) (

         Alloc a = get_allocator ();

         Nodeptr p = Node_alloc (a). Allocate (1);

         Try (

           A.construct (p-> val, x);

         )

         Catch (...) (

           Node_alloc (a). Deallocate (p, 1);

           Throw;

         )

         Voidptr_alloc (a). Construct (p-> next, Voidptr (next));

         Return p;

       )

       ...

     );

     Finally, do you think so hard to prevent access to the benefits of such a small worth too, remind: Just because you can write an allocator use of the containers, does not mean you have to, or you should. Sometimes you may write a special dependent on the memory allocation strategy of containers, such as disk-based complex to the simple B-tree container or to my book described the block type. Even if you have to write a allocator use of the containers, you may not need to support the types of indicators. You can write a container, and asked all user-defined allocator use of general guideline, and clearly stated in the document the limit. Not everything should be completely generic.

Looking to the future
     If you wanted to write a simple such as malloc_allocator allocator, it should have no difficulty. (Premise that you are using a more modern compiler). However, if your heart more, - based on the memory pool or support non-standard types of indicators allocator - the situation is not satisfactory by comparison.

     If you want to use may be indicators of categories (pointer-like) type, it must support the operation? It must have a null value? If so, how that value should be written? You can use type conversion? How do you object indicators in the category of the general guidelines to convert? You have to consider the operation pointer parabolic abnormal? I am the last one in a number of assumptions C + + standard does not specify these assumptions wrong. These details are left to the specific criteria for the realization of even achieving a completely neglected optional pointer types is legitimate. C + + standard also left some unanswered questions, such as when an allocator of different examples not interchangeable, what will happen.

     Fortunately, the situation is not like a standard (§ 20.1.5, 4-5) as described in the glossary so terrible. Standards left no answers to these question, because in the formulation of standards, the C + + standardization committee failed to reach a consensus on the answer; allocator of the necessary experience did not exist. Each participate in the formulation of the people think that this is a temporary patch, which will certainly be the ambiguity in the future amendments were removed.

     Wait a minute, if you care about portability, the best option is to stay away from the target type, and, if you would be happy to accept some restrictions, we can safely use different objects, such as mempool_allocator entities such great disparities between allocator. All the realization of the standards of mainstream now in a side does not support the allocator, and the differences between different achieve little.

     As an allocator containers accepted as a template parameters, the constructor function containers accept as a parameter an allocator. Containers retain the parameters of a copy, copy and use this to deal with all the memory management; containers allocator structure once completed initialization function, lifelong unchanged.

     The only question is when you need to run a two containers in the memory management on the operational synergy, what will happen. In the standard library is the existence of two such operations: swap () (all containers) and std:: list:: splice (). Principle, can be several different approaches to them:

L allocator does not prohibit the equivalent of two containers between swap (), and splice ().

L in the swap (), and splice () Add an allocator equivalence test. If we do not equivalence, downgrading called copy () and assignment operators.

L only swap (): As with the data in the same container, they exchanged the allocator. (It is difficult to identify how to extend it to the splice. It also has created a problem: how to swap absence of a definition of assignment operators at things?)

     If you can avoid the possible use of different containers allocator swap (), and splice (), and all are safe. In practice, I have not found that this may result in serious constraints: you need to use strict security exercises such characteristics such as memory pool, but you may not want to choose not to mix different allocator containers.

     Partly because not familiar with C + + as part of the requirements of the standard is not satisfactory, the vast majority of the current allocator the use of very simple. Because C + + community to become more familiar with the allocator, and the standard has been clarified, we can look forward to the use of more complex will emerge.

Listing 1: A sample allocator based on malloc
Template <class T> class malloc_allocator

(

Public:

   Typedef T value_type;

   Typedef value_type * pointer;

   Typedef const value_type * const_pointer;

   Typedef value_type & reference;

   Typedef const value_type & const_reference;

   Typedef std:: size_t size_type;

   Typedef std:: ptrdiff_t difference_type;

  

   Template <class U>

   (Struct rebind typedef malloc_allocator <U> other;);

 

   Malloc_allocator () ()

   Malloc_allocator (const malloc_allocator &) ()

   Template <class U>

   Malloc_allocator (const malloc_allocator <U> &) ()

   ~ Malloc_allocator () ()

 

   Pointer address (reference x) const (return &x;)

   Const_pointer address (const_reference x) const (

     Return x;

   )

 

   Pointer allocate (size_type n, const_pointer = 0) (

     Void * p = std:: malloc (n * sizeof (T));

     If (! P)

       Throw std:: bad_alloc ();

     Return static_cast <pointer> (p);

   )

 

   Void deallocate (pointer p, size_type) (std:: free (p);)

 

   Size_type max_size () const (

     Return static_cast <size_type> (-1) / sizeof (T);

   )

 

   Void construct (pointer p, const value_type & x) (

     New (p) value_type (x);

   )

   Void destroy (pointer p) (p-> ~ value_type ();)

 

Private:

   Void operator = (const malloc_allocator &);

);

 

Template <> class malloc_allocator <void>

(

   Typedef void value_type;

   Typedef void * pointer;

   Typedef const void * const_pointer;

 

   Template <class U>

   (Struct rebind typedef malloc_allocator <U> other;);

);

 

 

Template <class T>

Inline bool operator == (const malloc_allocator <T> &,

                        Const malloc_allocator <T> &) (

   Return true;

)

 

Template <class T>

Inline bool operator! = (Const malloc_allocator <T> &,

                        Const malloc_allocator <T> &) (

   Return false;

)

 

Note:
[1] You can see an example of a pool allocator in the open source SGI Pro64TM compiler, http://oss.sgi.com/projects/Pro64/.

[2] Why the funny template keyword in that expression? It's an annoying little technicality; like typename, it helps the compiler resolve a parsing ambiguity. The problem is that when A is a template parameter, and the compiler sees an expression like A: : B <T>, the compiler doesn't know anything about A's members. Should it assume that B <T> is a member template, or should it assume that B is an ordinary member variable and that <is just a less than sign ? A human reader knows which way to interpret it, but the compiler doesn't. You need to put in template to force the first interpretation. Formally, the rule (in § 14.2 of the C + + Standard) is: "When the name of a member template specialization appears after. or -> in a postfix-expression, or after nested-name-specifier in a qualified-id, and the postfix-expression or qualified-id explicitly depends on a template-parameter (14.6.2) , the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template. "
Previous:Based on the Visual C + + 6.0 Programming DDL
Next:VC++ mail volume derived Foxmail
User Reviews
Site Search
Related Articles
Recommended article
AD