Team LiB
Previous Section Next Section

16.4. Class Template Members

16.4. 类模板成员

So far we have seen only how to declare the interface members of our Queue class template. In this section, we'll look at how we might implement the class.

到目前为止,我们只介绍了怎样声明 Queue 类模板的接口成员,本节将介绍怎样实现该类。

The standard library implements queue as an adaptor (Section 9.7, p. 348) on top of another container. To emphasize the programming points involved in using a lower-level data structure, we'll implement our Queue class as a linked list. In practice, using a library container in our implementation would probably be a better decision.

标准库将 queue 实现为其他容器之上的适配器(第 9.7 节)。为了强调在使用低级数据结构中涉及的编程要点,我们将 Queue 实现为链表。实际上,在我们的实现中使用标准库容器可能是一个更好的决定。



Queue Implementation Strategy

Queue 的实现策略

Our implementation, shown in Figure 16.1 on the next page, uses two classes:

图 16.1 所示,我们的实现使用两个类:

Figure 16.1. Queue Implementation


  1. Class QueueItem will represent a node in Queue's linked list. This class has two data members: item and next:

    QueueItem 类表示 Queue 的链表中的节点,该类有两个数据成员 itemnext

    • item holds the value of the element in the Queue; its type varies with each instance of Queue.

      item 保存 Queue 中元素的值,它的类型随 Queue 的每个实例而变化。

    • next is a pointer to the next QueueItem object in the queue.

      next 是队列中指向下一 QueueItem 对象的指针。

    Each element in the Queue is stored in a QueueItem object.

    Queue 中的每个元素保存在一个 QueueItem 对象中。

  2. Class Queue will provide the interface functions described in Section 16.1.2 (p. 627). The Queue class will also have two data members: head and tail. These members are pointers to QueueItem.

    Queue 类将提供第 16.1.2 节描述的接口函数,Queue 类也有两个数据成员:headtail,这些成员是 QueueItem 指针。

As do the standard containers, our Queue class will copy the values it's given.

像标准容器一样,Queue 类将复制指定给它的值。

The QueueItem Class

QueueItem

We'll start our implementation by writing the QueueItem class:

首先编写 QueueItem 类:

     template <class Type> class QueueItem {
     // private class: no public section
         QueueItem(const Type &t): item(t), next(0) { }
         Type item;           // value stored in this element
         QueueItem *next;     // pointer to next element in the Queue
     };

As it stands, this class is already complete: It holds two data elements, which its constructor initializes. Like Queue, QueueItem is a class template. The class uses its template parameter to name the type of its item member. The value of each element in the Queue will be stored in item.

这个类似乎已经差不多完整了:它保存由其构造函数初始化的两个数据成员。像 Queue 类一样,QueueItem 是一个类模板,该类使用模板形参指定 item 成员的类型,Queue 中每个元素的值将保存在 item 中。

Each time we instantiate a Queue class, the same version of QueueItem will be instantiated as well. For example, if we create Queue<int>, then a companion class, QueueItem<int>, will be instantiated.

每当实例化一个 Queue 类的时候,也将实例化 QueueItem 的相同版本。例如,如果创建 Queue<int>,则将实例化一个伙伴类 QueueItem<int>

Class QueueItem is a private classit has no public interface. We intend this class to be used to implement Queue and have not built it for general use. Hence, it has no public members. We'll need to make class Queue a friend of QueueItem so that its members can access the members of QueueItem. We'll see how to do so in Section 16.4.4 (p. 658).

QueueItem 类为私有类——它没有公用接口。我们这个类只是为实现 Queue,并不想用于一般目的,因此,它没有公用成员。需要将 Queue 类设为 QueueItem 类的友元,以便 Queue 类成员能够访问 QueueItem 的成员。第 16.4.4 节将介绍怎样做。

Inside the scope of a class template, we may refer to the class using its unqualified name.

在类模板的作用域内部,可以用它的非限定名字引用该类。



The Queue Class

Queue

We can now flesh out our Queue class:

现在充实 Queue 类:

     template <class Type> class Queue {
     public:
         // empty Queue
         Queue(): head(0), tail(0) { }
         // copy control to manage pointers to QueueItems in the Queue
         Queue(const Queue &Q): head(0), tail(0)
                                       { copy_elems(Q); }
         Queue& operator=(const Queue&);
         ~Queue() { destroy(); }
              // return element from head of Queue
         // unchecked operation: front on an empty Queue is undefined
         Type& front()             { return head->item; }
         const Type &front() const { return head->item; }
         void push(const Type &);       // add element to back of Queue
         void pop ();                    // remove element from head of Queue
         bool empty () const {           // true if no elements in the Queue
             return head == 0;
         }
     private:
         QueueItem<Type> *head;         // pointer to first element in Queue
         QueueItem<Type> *tail;         // pointer to last element in Queue
         // utility functions used by copy constructor, assignment, and destructor
         void destroy();                // delete all the elements
         void copy_elems(const Queue&); // copy elements from parameter
     };

In addition to the interface members, we have added the three copy-control members (Chapter 13) and associated utility functions used by those members. The private utility functions destroy and copy_elems will do the work of freeing the elements in the Queue and copying elements from another Queue into this one. The copy-control members are needed to manage the data members, head and tail, which are pointers to the first and last elements in the Queue. These elements are values of type QueueItem<Type>.

除了接口成员之外,还增加了三个复制控制成员(第十三章)以及那些成员所用的相关实用函数。private 实用函数 destroycopy_elems 将完成释放 Queue 中的元素以及从另一 Queue 复制元素到这个 Queue 的任务。复制控制成员用于管理数据成员 headtailheadtail 是指向 Queue 中首尾元素的指针,这些成员是 QueueItem<Type> 类型的值。

The class implements several of its member functions:

Queue 类实现了几个成员函数:

  • The default constructor sets both head and tail pointers to zero, indicating that the Queue is currently empty.

    默认构造函数,将 headtail 指针置 0,指明当前 Queue 为空。

  • The copy constructor initializes head and tail, and calls copy_elems to copy the elements from its initializer.

    复制构造函数,初始化 headtail,并调用 copy_elems 从它的初始器复制元素。

  • The front functions return the value at the head of the Queue. These functions do no checking: As with the analogous operations in the standard queue, users may not run front on an empty Queue.

    几个 front 函数,返回头元素的值。这些函数不进行检查:像标准 queue 中的类似操作一样,用户不能在空 Queue 上运行 front 函数。

  • The empty function returns the result of comparing head with zero. If head is zero, the Queue is empty; otherwise, it is not.

    empty 函数,返回 head 与 0 的比较结果。如果 head 为 0,Queue 为空;否则,Queue 是非空的。

References to a Template Type in the Scope of the Template

模板作用域中模板类型的引用

For the most part, this class definition should be familiar. It differs little from other classes that we have defined. What is new is the use (or lack thereof) of the template type parameter in references to the Queue and QueueItem types.

这个类的主要部分应该是我们熟悉的。它只与我们已经定义过的类有少许区别。新的内容是 Queue 类型和 QueueItem 类型的引用中对模板类型形参的使用(或缺少)。

Ordinarily, when we use the name of a class template, we must specify the template parameters. There is one exception to this rule: Inside the scope of the class itself, we may use the unqualified name of the class template. For example, in the declarations of the default and copy constructor the name Queue is a shorthand notation that stands for Queue<Type>. Essentially the compiler infers that when we refer to the name of the class, we are referring to the same version. Hence, the copy constructor definition is really equivalent to writing:

通常,当使用类模板的名字的时候,必须指定模板形参。这一规则有个例外:在类本身的作用域内部,可以使用类模板的非限定名。例如,在默认构造函数和复制构造函数的声明中,名字 QueueQueue<Type> 缩写表示。实质上,编译器推断,当我们引用类的名字时,引用的是同一版本。因此,复制构造函数定义其实等价于:

     Queue<Type>(const Queue<Type> &Q): head(0), tail(0)
                 { copy_elems(Q); }

The compiler performs no such inference for the template parameter(s) for other templates used within the class. Hence, we must specify the type parameter when declaring pointers to the companion QueueItem class:

编译器不会为类中使用的其他模板的模板形参进行这样的推断,因此,在声明伙伴类 QueueItem 的指针时,必须指定类型形参:

     QueueItem<Type> *head;    // pointer to first element in Queue
     QueueItem<Type> *tail;    // pointer to last element in Queue

These declarations say that for a given instantiation of class Queue, head and tail point to an object of type QueueItem instantiated for the same template parameter. That is, the type of head and tail inside the Queue<int> instantiation is QueueItem<int>*. It would be an error to omit the template parameter in the definition of the head and tail members:

这些声明指出,对于 Queue 类的给定实例化,headtail 指向为同一模板形参实例化的 QueueItem 类型的对象,即,在 Queue<int> 实例化的内部,headtail 的类型是 QueueItem<int>*。在 headtail 成员的定义中省略模板形参将是错误的:

     QueueItem *head;        // error: which version of QueueItem?
     QueueItem *tail;        // error: which version of QueueItem?

Exercises Section 16.4

Exercise 16.30:

Identify which, if any, of the following class template declarations (or declaration pairs) are illegal.

如果有,指出下面类模板声明(或声明对)中哪些是非法的。

     (a) template <class Type> class C1;
         template <class Type, int size> class C1;
     (b) template <class T, U, class V> class C2;
     (c) template <class C1, typename C2> class C3 { };
     (d) template <typename myT, class myT> class C4 { };
     (e) template <class Type, int *ptr> class C5;
         template <class T, int *pi> class C5;

Exercise 16.31:

The following definition of List is incorrect. How would you fix it?

下面 List 的定义不正确,怎样改正?

     template <class elemType> class ListItem;
     template <class elemType> class List {
     public:
         List<elemType>();
         List<elemType>(const List<elemType> &);
         List<elemType>& operator=(const List<elemType> &);
         ~List();
         void insert(ListItem *ptr, elemType value);
         ListItem *find(elemType value);
     private:
         ListItem *front;
         ListItem *end;
     };


16.4.1. Class-Template Member Functions

16.4.1. 类模板成员函数

The definition of a member function of a class template has the following form:

类模板成员函数的定义具有如下形式:

  • It must start with the keyword template followed by the template parameter list for the class.

    必须以关键字 template 开关,后接类的模板形参表。

  • It must indicate the class of which it is a member.

    必须指出它是哪个类的成员。

  • The class name must include its template parameters.

    类名必须包含其模板形参。

From these rules, we can see that a member function of class Queue defined outside the class will start as

从这些规则可以看到,在类外定义的 Queue 类的成员函数的开关应该是:

     template <class T> ret-type Queue<T>::member-name

The destroy Function
destroy 函数

To illustrate a class template member function defined outside its class, let's look at the destroy function:

为了举例说明在类外定义的类模板成员函数,我们来看 destroy 函数:

     template <class Type> void Queue<Type>::destroy()
     {
         while (!empty())
             pop();
     }

This definition can be read from left to right as:

这个定义可以从左至右读作:

  • Defining a function template with a single type parameter named Type

    用名为 Type 的类型形参定义一个函数模板;

  • that returns void,

    它返回 void

  • which is in the scope of the Queue<Type> class template.

    它是在类模板 Queue<Type> 的作用域中。

The use of Queue<Type> preceding the scope operator (::) names the class to which the member function belongs.

在作用域操作符(::)之前使用的 Queue<Type> 指定成员函数所属的类。

Following the member-function name is the function definition. In the case of destroy, the function body looks very much like an ordinary nontemplate function definition. Its job is to walk the list of entries in this Queue, calling pop to remove each item.

跟在成员函数名之后的是函数定义。在 destroy 的例子中,函数体看来很普通的非模板函数定义,它的工作是遍历这个 Queue 的每个分支,调用 pop 除去每一项。

The pop Function
pop 函数

The pop member removes the value at the front of the Queue:

pop 成员的作用是除去 Queue 的队头值:

     template <class Type> void Queue<Type>::pop()
     {
         // pop is unchecked: Popping off an empty Queue is undefined
         QueueItem<Type>* p = head; // keep pointer to head so we can delete it
         head = head->next;         // head now points to next element
         delete p;                  // delete old head element
      }

The pop function assumes that users do not call pop on an empty Queue. The job of pop is to remove the element at the start of the Queue. We must reset the head pointer to point to the next element in the Queue, and then delete the element that had been at the head. The only tricky part is remembering to keep a separate pointer to that element so we can delete it after resetting the head pointer.

pop 函数假设用户不会在空 Queue 上调用 poppop 的工作是除去 Queue 的头元素。必须重置 head 指针以指向 Queue 中的下一元素,然后删除 head 位置的元素。唯一有技巧的部分是记得保持指向该元素的一个单独指针,以便在重置 head 指针之后可以删除元素。

The push Function
push 函数

The push member places a new item at the back of the queue:

push 成员将新项放在队列末尾:

     template <class Type> void Queue<Type>::push(const Type &val)
     {
         // allocate a new QueueItem object
         QueueItem<Type> *pt = new QueueItem<Type>(val);
         // put item onto existing queue
         if (empty())
             head = tail = pt; // the queue now has only one element
         else {
             tail->next = pt; // add new element to end of the queue
             tail = pt;
         }
     }

This function starts by allocating a new QueueItem, which is initialized from the value we were passed. There's actually a surprising bit of work going on in this statement:

这个函数首先分配新的 QueueItem 对象,用传递的值初始化它。这里实际上有些令人惊讶的工作,陈述如下:

  1. The QueueItem constructor copies its argument into the QueueItem's item member. As do the standard containers, our Queue class stores copies of the elements it is given.

    QueueItem 构造函数将实参复制到 QueueItem 对象的 item 成员。像标准容器所做的一样,Queue 类存储所给元素的副本。

  2. If item is a class type, the initialization of item uses the copy constructor of whatever type item has.

    如果 item 为类类型,item 的初始化使用 item 所具有任意类型的复制构造函数。

  3. The QueueItem constructor also initializes the next pointer to 0 to indicate that this element points to no other QueueItem.

    QueueItem 构造函数还将 next 指针初始化为 0,以指出该元素没有指向其他 QueueItem 对象。

Because we're adding the element at the end of the Queue, setting next to 0 is eactly what we want.

因为将在 Queue 的末尾增加元素,将 next 置 0 正是我们所希望的。

Having created and initialized a new element, we must next hook it into the Queue. If the Queue is empty, then both head and tail should point to this new element. If there are already other elements in the Queue, then we make the current tail element point to this new element. The old tail is no longer the last element, which we indicate by making tail point to the newly constructed element as well.

创建和初始化新元素之后,必须将它链入 Queue。如果 Queue 为空,则 headtail 都应该指向这个新元素。如果 Queue 中已经有元素了,则使当前 tail 元素指向这个新元素。旧的 tail 不再是最后一个元素了,这也是通过使 tail 指向新构造的元素指明的。

The copy Function
copy_elems 函数

Aside from the assignment operator, which we leave as an exercise, the only remaining function to write is copy_elems. This function is designed to be used by the assignment operator and copy constructor. Its job is to copy the elements from its parameter into this Queue:

我们将赋值操作符的实现留作习题,剩下要编写的函数只有 copy_elems 了。设计该函数的目的是供赋值操作符和复制构造函数使用,它的工作是从形参中复制元素到这个 Queue

     template <class Type>
     void Queue<Type>::copy_elems(const Queue &orig)
     {
         // copy elements from orig into this Queue
         // loop stops when pt == 0, which happens when we reach orig.tail
         for (QueueItem<Type> *pt = orig.head; pt; pt = pt->next)
             push(pt->item); // copy the element
      }

We copy the elements in a for loop that starts by setting pt equal to the parameter's head pointer. The for continues until pt is 0, which happens after we get to the element that is the last one in orig. For each element in orig, we push a copy of value in that element onto this Queue and advance pt to point to the next element in orig.

for 循环中复制元素,for 循环始于将 pt 设为等于形参的 head 指针。循环进行直至获得 orig 中最后一个元素之后,pt 为 0。对于 orig 中的每个元素,将该元素值的副本 push 到这个 Queue,并推进 pt 以指向 orig 中的下一元素。

Instantiation of Class-Template Member Functions
类模板成员函数的实例化

Member functions of class templates are themselves function templates. Like any other function template, a member function of a class template is used to generate instantiations of that member. Unlike other function templates, the compiler does not perform template-argument deduction when instantiating class template member functions. Instead, the template parameters of a class template member function are determined by the type of the object on which the call is made. For example, when we call the push member of an object of type Queue<int>, the push function that is instantiated is

类模板的成员函数本身也是函数模板。像任何其他函数模板一样,需要使用类模板的成员函数产生该成员的实例化。与其他函数模板不同的是,在实例化类模板成员函数的进修,编译器不执行模板实参推断,相反,类模板成员函数的模板形参由调用该函数的对象的类型确定。例如,当调用 Queue<int> 类型对象的 push 成员时,实例化的 push 函数为

     void Queue<int>::push(const int &val)

The fact that member-function template parameters are fixed by the template arguments of the object means that calling a class template member function is more flexible than comparable calls to function templates. Normal conversions are allowed on arguments to function parameters that were defined using the template parameter:

对象的模板实参能够确定成员函数模板形参,这一事实意味着,调用类模板成员函数比调用类似函数模板更灵活。用模板形参定义的函数形参的实参允许进行常规转换:

     Queue<int> qi; // instantiates class Queue<int>
     short s = 42;
     int i = 42;
     // ok: s converted to int and passed to push
     qi.push(s); // instantiates Queue<int>::push(const int&)
     qi.push(i); // uses Queue<int>::push(const int&)
     f(s);       // instantiates f(const short&)
     f(i);       // instantiates f(const int&)

When Classes and Members Are Instantiated
何时实例化类和成员

Member functions of a class template are instantiated only for functions that are used by the program. If a function is never used, then that member function is never instantiated. This behavior implies that types used to instantiate a template need to meet only the requirements of the operations that are actually used. As an example, recall the sequential container constructor (Section 9.1.1, p. 309) that takes only a size parameter. That constructor uses the default constructor for the element type. If we have a type that does not define the default constructor, we may still define a container to hold this type. However, we may not use the constructor that takes only a size.

类模板的成员函数只有为程序所用才进行实例化。如果某函数从未使用,则不会实例化该成员函数。这一行为意味着,用于实例化模板的类型只需满足实际使用的操作的要求。第 9.1.1 节中只接受一个容量形参的顺序容器构造函数就是这样的例子,该构造函数使用元素类型的默认构造函数。如果有一个没有定义默认构造函数的类型,仍然可以定义容器来保存该类型,但是,不能使用只接受一个容量的构造函数。

When we define an object of a template type, that definition causes the class template to be instantiated. Defining an object also instantiates whichever constructor was used to initialize the object, along with any members called by that constructor:

定义模板类型的对象时,该定义导致实例化类模板。定义对象也会实例化用于初始化该对象的任一构造函数,以及该构造函数调用的任意成员:

     // instantiates Queue<int> class and Queue<int>::Queue()
     Queue<string> qs;
     qs.push("hello"); // instantiates Queue<int>::push

The first statement instantiates the Queue<string> class and its default constructor. The next statement instantiates the push member function.

第一个语句实例化 Queue 类及其默认构造函数,第二个语句实例化 push 成员函数。

The instantiation of the push member:

push 成员的实例化:

     template <class Type> void Queue<Type>::push(const Type &val)
     {
          // allocate a new QueueItem object
          QueueItem<Type> *pt = new QueueItem<Type>(val);
          // put item onto existing queue
          if (empty())
              head = tail = pt;    // the queue now has only one element
          else {
              tail->next = pt;     // add new element to end of the queue
              tail = pt;
          }
     }

in turn instantiates the companion QueueItem<string> class and its constructor.

将依次实例化伙伴类 QueueItem<string> 及其构造函数。

The QueueItem members in Queue are pointers. Defining a pointer to a class template doesn't instantiate the class; the class is instantiated only when we use such a pointer. Thus, QueueItem is not instantiated when we create a Queue object. Instead, the QueueItem class is instanatiated when a Queue member such as front, push, or pop is used.

Queue 类中的 QueueItem 成员是指针。类模板的指针定义不会对类进行实例化,只有用到这样的指针时才会对类进行实例化。因此,在创建 Queue 对象进不会实例化 QueueItem 类,相反,在使用诸如 frontpushpop 这样的 Queue 成员时才实例化 QueueItem 类。

Exercises Section 16.4.1

Exercise 16.32:

Implement the assignment operator for class Queue.

Queue 类实现赋值操作符。

Exercise 16.33:

Explain how the next pointers in the newly created Queue get set during the copy_elems function.

解释在 copy_elems 函数中新创建的 Queue 对象中的 next 指针怎样设置。

Exercise 16.34:

Write the member function definitions of the List class that you defined for the exercises in Section 16.1.2 (p. 628).

编写第 16.1.2 节习题中定义的 List 类的成员函数定义。

Exercise 16.35:

Write a generic version of the CheckedPtr class described in Section 14.7 (p. 526).

编写第 14.7 节中描述的 CheckedPtr 类的泛型版本。


16.4.2. Template Arguments for Nontype Parameters

16.4.2. 非类型形参的模板实参

Now that we've seen more about how class templates are implemented, we can look at nontype parameters for class templates. We'll do so by defining a new version of the Screen class first introduced in Chapter 12. In this case, we'll redefine Screen to be a template, parameterized by its height and width:

我们已经了解了如何实现类模板,现在来看看类模板的非类型形参。我们将为第十二章引入的 Screen 类定义一个新版本,借引介绍类模板的非类型形参。在这个例子中,将 Screen 类重新定义为模板,以高度和宽度为形参。

     template <int hi, int wid>
     class Screen {
     public:
         // template nontype parameters used to initialize data members
         Screen(): screen(hi * wid, '#'), cursor (0),
                   height(hi), width(wid) { }
         // ...
     private:
         std::string            screen;
         std::string::size_type cursor;
         std::string::size_type height, width;
     };

This template has two parameters, both of which are nontype parameters. When users define Screen objects, they must provide a constant expression to use for each of these parameters. The class uses these parameters in the default constructor to set the size of the default Screen.

这个模板有两个形参,均为非类型形参。当用户定义 Screen 对象时,必须为每个形参提供常量表达式以供使用。类在默认构造函数中使用这些形参设置默认 Screen 的尺寸。

As with any class template, the parameter values must be explicitly stated whenever we use the Screen type:

像任意类模板一样,使用 Screen 类型时必须显式声明形参值:

     Screen<24,80> hp2621; // screen 24 lines by 80 characters

The object hp2621 uses the template instantiation Screen<24, 80>. The template argument for hi is 24, and the argument for wid is 80. In both cases, the template argument is a constant expression.

对象 hp2621 使用模板实例化 Screen<24, 80>hi 的模板实参是 24,而 wid 的模板实参是 80,两种情况下,模板实参都是常量表达式。

Nontype template arguments must be compile-time constant expressions.

非类型模板实参必须是编译时常量表达式。



Exercises Section 16.4.2

Exercise 16.36:

Explain what instantiations, if any, are caused by each labeled statement.

每个带标号的语句,会导致实例化吗?如果会,解释为什么。

     template <class T> class Stack { };
     void f1(Stack<char>);                   // (a)
     class Exercise {
         Stack<double> &rsd;                 // (b)
         Stack<int> si;                      // (c)
     };
     int main() {
         Stack<char> *sc;                    // (d)
         f1(*sc);                            // (e)
         int iObj = sizeof(Stack< string >); // (f)
     }

Exercise 16.37:

Identify which, if any, of the following template instantiations are valid. Explain why the instantiation isn't valid.

下面哪些模板实例化是有效的?解释为什么实例化无效。

     template <class T, int size> class Array { /* . . . */ };
     template <int hi, int wid> class Screen { /* . . . */ };
     (a) const int hi = 40, wi = 80; Screen<hi, wi+32> sObj;
     (b) const int arr_size = 1024; Array<string, arr_size> a1;
     (c) unsigned int asize = 255; Array<int, asize> a2;
     (e) const double db = 3.1415; Array<double, db> a3;


16.4.3. Friend Declarations in Class Templates

16.4.3. 类模板中的友元声明

There are three kinds of friend declarations that may appear in a class template. Each kind of declaration declares friendship to one or more entities:

在类模板中可以出现三种友元声明,每一种都声明了与一个或多个实体友元关系:

  1. A friend declaration for an ordinary nontemplate class or function, which grants friendship to the specific named class or function.

    普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数。

  2. A friend declaration for a class template or function template, which grants access to all instances of the friend.

    类模板或函数模板的友元声明,授予对友元所有实例的访问权。

  3. A friend declaration that grants access only to a specific instance of a class or function template.

    只授予对类模板或函数模板的特定实例的访问权的友元声明。

Ordinary Friends
普通友元

A nontemplate class or function can be a friend to a class template:

非模板类或非模板函数可以是类模板的友元:

     template <class Type> class Bar {
         // grants access to ordinary, nontemplate class and function
         friend class FooBar;
         friend void fcn();
         // ...
     };

This declaration says that the members of FooBar and the function fcn may access the private and protected members of any instantiation of class Bar.

这个声明是说,FooBar 的成员和 fcn 函数可以访问 Bar 类的任意实例的 private 成员和 protected 成员。

General Template Friendship
一般模板友元关系

A friend can be a class or function template:

友元可以是类模板或函数模板:

     template <class Type> class Bar {
         // grants access to Foo1 or templ_fcn1 parameterized by any type
         template <class T> friend class Foo1;
         template <class T> friend void templ_fcn1(const T&);
         // ...
     };

These friend declarations use a different type parameter than does the class itself. That type parameter refers to the type parameter of Foo1 and templ_fcn1. In both these cases, an unlimited number of classes and functions are made friends to Bar. The friend declaration for Foo1 says that any instance of Foo1 may access the private elements of any instance of Bar. Similarly, any instance of templ_fcn1 may access any instance of Bar.

这些友元声明使用与类本身不同的类型形参,该类型形参指的是 Foo1temp1_fcn1 的类型形参。在这两种情况下,都将没有数目限制的类和函数设为 Bar 的友元。Foo1 的友元声明是说,Foo1 的友元声明是说,Foo1 的任意实例都可以访问 Bar 的任意实例的私有元素,类似地,temp_fcn1 的任意实例可以访问 Bar 的任意实例。

This friend declaration establishes a one-to-many mapping between each instantiation of Bar and its friends, Foo1 and templ_fcn1. For each instantiation of Bar, all instantiations of Foo1 or templ_fcn1 are friends.

这个友元声明在 Bar 与其友元 Foo1temp1_fcn1 的每个实例之间建立了一对多的映射。对 Bar 的每个实例而言,Foo1temp1_fcn1 的所有实例都是友元。

Specific Template Friendship
特定的模板友元关系

Rather than making all instances of a template a friend, a class can grant access to only a specific instance:

除了将一个模板的所有实例设为友元,类也可以只授予对特定实例的访问权:

     template <class T> class Foo2;
     template <class T> void templ_fcn2(const T&);
     template <class Type> class Bar {
          // grants access to a single specific instance parameterized by char*
          friend class Foo2<char*>;
          friend void templ_fcn2<char*>(char* const &);
          // ...
     };

Even though Foo2 itself is a class template, friendship is extended only to the specific instance of Foo2 that is parameterized by char*. Similarly, the friend declaration for templ_fcn2 says that only the instance of that function parameterized by char* is a friend to class Bar. The specific instantiations of Foo2 and templ_fcn2 parameterized by char* can access every instantiation of Bar.

即使 Foo2 本身是类模板,友元关系也只扩展到 Foo2 的形参类型为 char* 的特定实例。类似地,temp1_fcn2 的友元声明是说,只有形参类型为 char* 的函数实例是 Bar 类的友元。形参类型为 char*Foo2temp1_fcn2 的特定实例可以访问 Bar 的每个实例。

More common are friend declarations of the following form:

下面形式的友元声明更为常见:

     template <class T> class Foo3;
     template <class T> void templ_fcn3(const T&);
     template <class Type> class Bar {
         // each instantiation of Bar grants access to the
         // version of Foo3 or templ_fcn3 instantiated with the same type
         friend class Foo3<Type>;
         friend void templ_fcn3<Type>(const Type&);
         // ...
     };

These friends define friendship between a particular instantiation of Bar and the instantiation of Foo3 or templ_fcn3 that uses the same template argument. Each instantiation of Bar has a single associated Foo3 and templ_fcn3 friend:

这些友元定义了 Bar 的特定实例与使用同一模板实参的 Foo3temp1_fcn3 的实例之间的友元关系。每个 Bar 实例有一个相关的 Foo3temp1_fcn3 友元:

     Bar<int> bi;    // Foo3<int> and templ_fcn3<int> are friends
     Bar<string> bs; // Foo3<string>, templ_fcn3<string> are friends

Only those versions of Foo3 or templ_fcn3 that have the same template argument as a given instantiation of Bar are friends. Thus, Foo3<int> may access the private parts of Bar<int> but not of Bar<string> or any other instantiation of Bar.

只有与给定 Bar 实例有相同模板实参的那些 Foo3temp1_fcn3 版本是友元。因此,Foo3<int> 可以访问 Bar<int> 的私有部分,但不能访问 Bar<string> 或者任意其他 Bar 实例的私有部分。

Declaration Dependencies
声明依赖性

When we grant access to all instances of a given template, there need not be a declaration for that class or function template in scope. Essentially, the compiler treats the friend declaration as a declaration of the class or function as well.

当授予对给定模板的实例的访问权时候,在作用域中不需要存在该类模板或函数模板的声明。实质上,编译器将友元声明也当作类或函数的声明对待。

When we want to restrict friendship to a specific instantiation, then the class or function must have been declared before it can be used in a friend declaration:

想要限制对特定实例化的友元关系时,必须在可以用于友元声明之前声明类或函数:

     template <class T> class A;
     template <class T> class B {
     public:
         friend class A<T>;      // ok: A is known to be a template
         friend class C;         // ok: C must be an ordinary, nontemplate class
         template <class S> friend class D; // ok: D is a template
         friend class E<T>;      // error: E wasn't declared as a template
         friend class F<int>;    // error: F wasn't declared as a template
      };

If we have not previously told the compiler that the friend is a template, then the compiler will infer that the friend is an ordinary nontemplate class or function.

如果没有事先告诉编译器该友元是一个模板,则编译器将认为该友元是一个普通非模板类或非模板函数。

16.4.4. Queue and QueueItem Friend Declarations

16.4.4. QueueQueueItem 的友元声明

Our QueueItem class is not intended to be used by the general program: All its members are private. In order for Queue to use QueueItem, QueueItem must make Queue a friend.

QueueItem 类不打算为一般程序所用:它的所有成员都是私有的。为了让 Queue 类使用 QueueItem 灰,QueueItem 类必须将 Queue 类设为友元。

Making a Class Template a Friend
将类模板设为友元

As we have just seen, when making a class template a friend, the class designer must decide how wide to make that friendship. In the case of QueueItem, we need to decide whether QueueItem should grant friendship to all Queue instances or only to a specific instance.

像我们已经看到的,将类模板设为友元的进修,类设计者必须决定友元关系应设置多广。在 QueueItem 类的例子中,需要决定 QueueItem 类应该将友元关系授予所有的 Queue 类实例,还是只授予特定实例。

Making every Queue a friend of each QueueItem is too broad. It makes no sense to allow a Queue instantiated with the type string to access members of a QueueItem instantiated with type double. The Queue<string> instantiation should be a friend only to the instantiation of the QueueItem for strings. That is, we want a one-to-one mapping between a Queue and QueueItem for each type of Queue that is instantiated:

将每个 Queue 类设为每个 QueueItem 类的友元太宽泛了,允许用 string 类型实例化的 Queue 类去访问用 double 类型实例化的 QueueItem 类的成员是没有意义的。Queue<string> 实例只应该是用 string 实例化的 QueueItem 类的友元,即,对于实例化的 Queue 类的每种类型,我们想要 Queue 类和 QueueItem 类之间的一对一映射:

     // declaration that Queue is a template needed for friend declaration in QueueItem
     template <class Type> class Queue;
     template <class Type> class QueueItem {
         friend class Queue<Type>;
         // ...
      };

This declaration establishes the desired one-to-one mapping; only the Queue class that is instantiated with the same type as QueueItem is made a friend.

这个声明建立了想要一对一映射,只将与 QueueItem 类用同样类型实例化的 Queue 类设为友元。

The Queue Output Operator
Queue 输出操作符

One operation that might be useful to add to our Queue interface is the ability to print the contents of a Queue object. We'll do so by providing an overloaded instance of the output operator. This operator will walk the list of elements in the Queue and print the value in each element. We'll print the elements inside a pair of brackets.

Queue 类接口中可能增加的一个有用操作,是输出 Queue 对象的内容的能力。提供输出操作符的重载实例,可以做到这一点。这个操作符将遍历 Queue 中的元素链表并输出每个元素的值,将在一对尖括号内输出元素。

Because we want to be able to print the contents of Queues of any type, we need to make the output operator a template as well:

因为希望能够输出任意类型 Queue 的内容,所以需要将输出操作符也设为模板:

     template <class Type>
     ostream& operator<<(ostream &os, const Queue<Type> &q)
     {
         os << "< ";
         QueueItem<Type> *p;
         for (p = q.head; p; p = p->next)
                 os << p->item << " ";
         os <<">";
         return os;
     }

If a Queue of type int contains the values 3, 5, 8, and 13, the output of this Queue displays as follows:

如果 int 类型的 Queue 包含值 3、5、8 和 13,这个 Queue 的输出显示如下:

     <3 5 8 13 >

If the Queue is empty, the for loop body is never executed. The effect will be to print an empty pair of brackets if the Queue is empty.

如果 Queue 为空,for 循环不执行。结果是输出一对空的尖括号。

Making a Function Template a Friend
将函数模板设为友元

The output operator needs to be a friend of both the Queue and QueueItem classes. It uses the head member of class Queue and the next and item members of class QueueItem. Our classes grant friendship to the specific instance of the output operator instantiated with the same type:

输出操作符需要成为 Queue 类和 QueueItem 类的友元。它使用 Queue 类的 head 成员和 QueueItem 类的 nextitem 成员。我们的类将友元关系授予用同样类型实例化的输出操作符的特定实例:

     // function template declaration must precede friend declaration in QueueItem
     template <class T>
     std::ostream& operator<<(std::ostream&, const Queue<T>&);
     template <class Type> class QueueItem {
         friend class Queue<Type>;
         // needs access to item and next
         friend std::ostream&
         operator<< <Type> (std::ostream&, const Queue<Type>&);
         // ...
     };
     template <class Type> class Queue {
         // needs access to head
         friend std::ostream&
         operator<< <Type> (std::ostream&, const Queue<Type>&);
     };

Each friend declaration grants access to the corresponding instantiation of the operator<<. That is, the output operator that prints a Queue<int> is a friend to class Queue<int> (and QueueItem<int>). It is not a friend to any other Queue type.

每个友元声明授予对对应 operator<< 实例的访问权,即输出 Queue<int> 的输出操作符是 Queue<int> 类(以及 QueueItem<int>) 类)的友元,它不是任意其他 Queue 类型的友元。

Type Dependencies and the Output Operator
类型依赖性与输出操作符

The Queue output operator<< relies on the operator<< of item to actually print each element:

Queue 类的输出 operator<< 依赖于 item 对象的 operator<< 实际输出每个元素:

     os << p->item << " ";

When we use p->item as an operand of the << operator, we are using the << defined for whatever type item has.

当使用 p->item 作为 << 操作符的操作数的时候,使用的是为 item 所属的任意类型而定义的 <<

This code is an example of a type dependency between Queue and the element type that Queue holds. In effect, each type bound to Queue that uses the Queue output operator must itself have an output operator. There is no language mechanism to specify or enforce that dependency in the definition of Queue itself. It is legal to create a Queue for a class that does not define the output operator but it is a compile-time (or link-time) error to print a Queue holding such a type.

此代码是 QueueQueue 保存的元素之间的类型依赖性的例子。实际上,绑定到 Queue 且使用 Queue 输出操作符的每种类型本身必须有输出操作符。没有语言机制指定或强制 Queue 自身定义中的依赖性。为没有定义输出操作符的类创建 Queue 对象是合法的,但输出保存这种类型的 Queue 对象会发生编译时(或链接时)错误。

Exercises Section 16.4.4

Exercise 16.38:

Write a Screen class template that uses nontype parameters to define the height and width of the Screen.

编写 Screen 类模板,使用非类型形参定义 Screen 的高度和宽度。

Exercise 16.39:

Implement input and output operators for the template Screen class.

Screen 模板类实现输入和输出操作符。

Exercise 16.40:

Which, if any, friends are necessary in class Screen to make the input and output operators work? Explain why each friend declaration, if any, was needed.

要使输入和输出操作符能够工作,Screen 类需要友元吗?如果需要,要哪些友元解释为什么需要每个友元声明。

Exercise 16.41:

The friend declaration for operator<< in class Queue was

Queue 类中的 operator<< 的友元声明是:

     friend std::ostream&
     operator<< <Type> (std::ostream&, const Queue<Type>&);

What would be the effect of writing the Queue parameter as const Queue& rather than const Queue<Type>&?

Queue 形参写为 const Queue& 而不是 const Queue<Type>&,会有什么结果?

Exercise 16.42:

Write an input operator that reads an istream and puts the values it reads into a Queue.

编写一个输入操作符,读一个 istream 对象并将读到的值放入一个 Queue 对象中。


16.4.5. Member Templates

16.4.5. 成员模板

Any class (template or otherwise) may have a member that is itself a class or function template. Such members are referred to as member templates. Member templates may not be virtual.

任意类(模板或非模板)可以拥有本身为类模板或函数模板的成员,这种成员称为成员模板,成员模板不能为虚。

One example of a member template is the assign (Section 9.3.8, p. 328) member of the standard containers. The version assign that takes two iterators uses a template parameter to represent the type of its iterator parameters. Another member template example is the container constructor that takes two iterators (Section 9.1.1, p. 307). This constructor and the assign member allow containers to be built from sequences of different but compatible element types and/or different container types. Having implemented our own Queue class, we now can understand the design of these standard container members a bit better.

成员模板的一个例子是标准容器的 assign 成员(第 9.3.8 节),接受两个迭代器的 assign 版本使用模板形参表示其迭代器形参的类型。另一个成员模板例子是接受两个迭代器的容器构造函数(第 9.1.1 节)。该构造函数和 assign 成员使我们能够从不同但兼容的元素类型序列和/或不同容器类型建立容器。实现了自己的 Queue 类之后,我们现在能够更好地理解这些标准容器成员的设计了。

Consider the Queue copy constructor: It takes a single parameter that is a reference to a Queue<Type>. If we wanted to create a Queue by copying elements from a vector, we could not do so; there is no conversion from vector to Queue. Similarly, if we wanted to copy elements from a Queue<short> into a Queue<int>, we could not do so. Again, even though we can convert a short to an int, there is no conversion from Queue<short> to Queue<int>. The same logic applies to the Queue assignment operator, which also takes a parameter of type Queue<Type>&.

考虑 Queue 类的复制构造函数:它接受一个形参,是 Queue<Type> 的引用。想要通过从 vector 对象中复制元素而创建 Queue 对象,是办不到的,因为没有从 vector 到 Queue 的转换。类似地,想要从 Queue<short> 复制元素到 Queue<int>,也办不到。同样的逻辑应用于赋值操作符,它也接受一个 Queue<Type>& 类型的形参。

The problem is that the copy constructor and assignment operator fix both the container and element type. We'd like to define a constructor and an assign member that allow both the container and element type to vary. When we need a parameter type to vary, we need to define a function template. In this case, we'll define the constructor and assign member to take a pair of iterators that denote a range in some other sequence. These functions will have a single template type parameter that represents an iterator type.

问题在于,复制构造函数和赋值操作符固定了容器和元素的类型。我们希望定义一个构造函数和一个 assign 成员,使容器类型和元素类型都能变化。需要形参类型变化的时候,就需要定义函数模板。在这个例子中,我们将定义构造函数和 assign 成员接受一对在其他序列指明范围的迭代器,这些函数将有一个表示迭代器类型的模板类型形参。

The standard queue class does not define these members: queue doesn't support building or assigning a queue from another container. We define these members here for illustration purposes only.

标准 queue 类没有定义这些成员:不支持从其他容器建立 queue 对象或给 queue 对象赋值。我们在这里定义这些成员只是为了举例说明。



Defining a Member Template
定义成员模板

A template member declaration looks like the declaration of any template:

模板成员声明看起来像任意模板的声明一样:

     template <class Type> class Queue {
     public:
         // construct a Queue from a pair of iterators on some sequence
         template <class It>
         Queue(It beg, It end):
               head(0), tail(0) { copy_elems(beg, end); }
         // replace current Queue by contents delimited by a pair of iterators
         template <class Iter> void assign(Iter, Iter);
         // rest of Queue class as before
     private:
         // version of copy to be used by assign to copy elements from iterator range
         template <class Iter> void copy_elems(Iter, Iter);
     };

The member declaration starts with its own template parameter list. The constructor and assign member each have a single template type parameter. These functions use that type parameter as the type for their function parameters, which are iterators denoting a range of elements to copy.

成员声明的开关是自己的模板形参表。构造函数和 assign 成员各有一个模板类型形参,这些函数使用该类型形参作为其函数形参的类型,它们的函数形参是指明要复制元素范围的迭代器。

Defining a Member Template Outside the Class
在类外部定义成员模板

Like nontemplate members, a member template can be defined inside or outside of its enclosing class or class template definition. We have defined the constructor inside the class body. Its job is to copy the elements from the iterator range formed by its iterator arguments. It does so by calling the iterator version of copy_elems to do the actual copy.

像非模板成员一样,成员模板可以定义在包含它的类或类模板定义的内部或外部。我们已经在类定义体内部定义了构造函数,它的工作是从迭代器实参形成的迭代器范围复制元素,实际复制工作是通过调用 copy_elems 的迭代器版本完成的。

When we define a member template outside the scope of a class template, we must include both template parameter lists:

     template <class T> template <class Iter>
     void Queue<T>::assign(Iter beg, Iter end)
     {
         destroy();            // remove existing elements in this Queue
         copy_elems(beg, end); // copy elements from the input range
     }

When a member template is a member of a class template, then its definition must include the class-template parameters as well as its own template parameters. The class-template parameter list comes first, followed by the member's own template parameter list. The definition of assign starts with

当成员模板是类模板的成员时,它的定义必须包含类模板形参以及自己的模板形参。首先是类模板形参表,后面接着成员自己的模板形参表。assign 函数定义的开头为

     template <class T> template <class Iter>

The first template parameter list template<class T>is that of the class template. The second template parameter list template<class Iter>is that of the member template.

第一个模板形参表 template<class T> 是类模板的,第二个模板形参表 template<class Iter> 是成员模板的。

The actions of our assign function are quite simple: It first calls destroy, which, as we've seen, frees the existing members of this Queue. The assign member then calls a new utility function named copy_elems to do the work of copying elements from the input range. That function is also a member template:

assign 函数的行为非常简单:它首先调用 destroy 函数,destroy 函数释放这个 Queue 的现在成员,然后 assign 成员调用名为 copy_elems 的新实用函数,完成从输入范围复制元素的工作。copy_elems 函数也是一个成员模板:

     template <class Type> template <class It>
     void Queue<Type>::copy_elems(It beg, It end)
     {
         while (beg != end) {
            push(*beg);
            ++beg;
         }
     }

The iterator version of copy_elems walks through an input range denoted by a pair of iterators. It calls push on each element in that range, which actually adds the element to the Queue.

copy_elems 的迭代器版本遍历由一对迭代器指定的输入范围,它对范围内的每个元素调用 push 函数,实际上由 push 函数将元素加入 Queue

Because assign erases elements in the existing container, it is essential that the iterators passed to assign refer to elements in a different container. The standard container assign members and iterator constructors have the same restrictions.

因为 assign 函数删除现在容器中的成员,所以传给 assign 函数的迭代器有必要引用不同容器中的元素。标准容器的 assign 成员和迭代器构造函数有相同的限制。



Member Templates Obey Normal Access Control
成员模板遵循常规访问控制

A member template follows the same access rules as any other class members. If the member template is private, then only member functions and friends of the class can use that member template. Because the function member template assign is a public member, it can be used by the entire program; copy_elems is private, so it can be accessed only by the friends and members of Queue.

成员模板遵循与任意其他类成员一样的访问规则。如果成员模板为私有的,则只有该类的成员函数和友元可以使用该成员模板。因为函数成员模板 assign 是公有的,所以整个程序都可以使用它:copy_elems 是私有的,所以只有 Queue 的友元和成员可以访问它。

Member Templates and Instantiation
成员模板和实例化

Like any other member, a member template is instantiated only when it is used in a program. The instantiation of member templates of class templates is a bit more complicated than the instantiation of plain member functions of class templates. Member templates have two kinds of template parameters: Those that are defined by the class and those defined by the member template itself. The class template parameters are fixed by the type of the object through which the function is called. The template parameters defined by the member act like parameters of ordinary function templates. These parameters are resolved through normal template argument deduction (Section 16.2.1, p. 637).

与其他成员一样,成员模板只有在程序中使用时才实例化。类模板的成员模板的实例化比类模板的普通成员函数的实例化要复杂一点。成员模板有两种模板形参:由类定义的和由成员模板本身定义的。类模板形参由调用函数的对象的类型确定,成员定义的模板形参的行为与普通函数模板一样。这些形参都通过常规模板实参推断(第 16.2.1 节)而确定。

To understand how instantiation works, let's look at uses of these members to copy and assign elements from an array of shorts or a vector<int>:

要理解实例化的原理,我们来看看使用这些成员从 short 数组或 vector<int>: 复制和赋值元素:

     short a[4] = { 0, 3, 6, 9 };
     // instantiates Queue<int>::Queue(short *, short *)
     Queue<int> qi(a, a + 4); // copies elements from a into qi
     vector<int> vi(a, a + 4);
     // instantiates Queue<int>::assign(vector<int>::iterator,
     //                                 vector<int>::iterator)
     qi.assign(vi.begin(), vi.end());

Because we are constructing an object of type Queue<int>, we know that the compiler will instantiate the iterator-based constructor for Queue<int>. The type of the constructor's own template parameter is deduced by the compiler from the type of a and a +4. That type is pointer to short. Thus, the definition of qi instantiates

因为所构造的是 Queue<int> 类型的对象,我们知道编译器将为 Queue<int> 实例化基于迭代器的构造函数。该构造函数本身模板形参的类型由编译器根据 aa+4 的类型推断,而该类型为 short 指针。因此,qi 的定义将实例化

     void Queue<int>::Queue(short *, short *);

The effect of this constructor is to copy the elements of type short from the array named a into qi.

这个构造函数的效果是,从名为 a 的数组中复制 short 类型的元素到 qi

The call to assign instantiates a member of qi, which has type Queue<int>. Thus, this call instantiates the Queue<int> member named assign. That function is itself a function template. As with any other function template, the compiler deduces the template argument for assign from the arguments to the call. The type deduced is vector<int>::iterator, meaning that this call instantiates

assign 的调用将实例化 qi 的成员。qi 具有 Queue<int> 类型,因此,这个调用将实例化名为 assignQueue<int> 成员。该函数本身是函数模板,像对任意其他函数模板一样,编译器从传给调用的实参推断 assign 的模板实参,推断得到的类型是 vector<int>::iterator,即,这个调用将实例化

     void Queue<int>::assign(vector<int>::iterator,
                             vector<int>::iterator);

16.4.6. The Complete Queue Class

16.4.6. 完整的 Queue

For completeness, here is the final definition of our Queue class:

为了完整起见,在这里给出 Queue 类的最终定义:

     // declaration that Queue is a template needed for friend declaration in QueueItem
     template <class Type> class Queue;
     // function template declaration must precede friend declaration in QueueItem
     template <class T>
     std::ostream& operator<<(std::ostream&, const Queue<T>&);
     template <class Type> class QueueItem {
         friend class Queue<Type>;
         // needs access to item and next
         friend std::ostream&     // defined on page 659
         operator<< <Type> (std::ostream&, const Queue<Type>&);
     // private class: no public section
         QueueItem(const Type &t): item(t), next(0) { }
         Type item;           // value stored in this element
         QueueItem *next;     // pointer to next element in the Queue
     };
     template <class Type> class Queue {
         // needs access to head
         friend std::ostream& // defined on page 659
         operator<< <Type> (std::ostream&, const Queue<Type>&);
     public:
         // empty Queue
         Queue(): head(0), tail(0) { }
         // construct a Queue from a pair of iterators on some sequence
         template <class It>
         Queue(It beg, It end):
               head(0), tail(0) { copy_elems(beg, end); }
         // copy control to manage pointers to QueueItems in the Queue
         Queue(const Queue &Q): head(0), tail(0)
                                       { copy_elems(Q); }
         Queue& operator=(const Queue&); // left as exercise for the reader
         ~Queue() { destroy(); }
         // replace current Queue by contents delimited by a pair of iterators
         template <class Iter> void assign(Iter, Iter);
         // return element from head of Queue
         // unchecked operation: front on an empty Queue is undefined
         Type& front()             { return head->item; }
         const Type &front() const { return head->item; }
         void push(const Type &);// defined on page 652
         void pop();             // defined on page 651
         bool empty() const {           // true if no elements in the Queue
             return head == 0;
     }
     private:
         QueueItem<Type> *head;   // pointer to first element in Queue
         QueueItem<Type> *tail;   // pointer to last element in Queue
     // utility functions used by copy constructor, assignment, and destructor
     void destroy();                // defined on page 651
     void copy_elems(const Queue&); // defined on page 652
     // version of copy to be used by assign to copy elements from iterator range
     // defined on page 662
     template <class Iter> void copy_elems(Iter, Iter);
     };
     // Inclusion Compilation Model: include member function definitions as well
     #include "Queue.cc"

Members that are not defined in the class itself can be found in earlier sections of this chapter; the comment following such members indicates the page on which the definition can be found.

未在类本身中定义的成员可在本章前面几节中找到,跟在这些成员后面的注释指出了可以在哪一节找到它们。

Exercises Section 16.4.6

Exercise 16.43:

Add the assign member and a constructor that takes a pair of iterators to your List class.

为你的 List 类增加 assign 成员和一个参数为一对迭代器的构造函数。

Exercise 16.44:

We implemented our own Queue class in order to illustrate how class templates are implemented. One way in which our implementation could be simplified would be to define Queue on top of one of the existing library container types. That way, we could avoid having to manage the allocation and deallocation of the Queue elements. Reimplement Queue using std::list to hold the actual Queue elements.

为了举例说明怎样实现类模板,我们实现了自己的 Queue 类。可以简化实现的一种方式可能是将 Queue 定义在一个现存的标准库容器类型之上,用这种方法,可以避免必须管理 Queue 元素的分配和回收。用 std::List 保存实际 Queue 元素,重新实现 Queue 类。


16.4.7. static Members of Class Templates

16.4.7. 类模板的 static 成员

A class template can declare static members (Section 12.6, p. 467) in the same way as any other class:

类模板可以像任意其他类一样声明 static 成员(第 12.6 节)。以下代码:

     template <class T> class Foo {
     public:
        static std::size_t count() { return ctr; }
        // other interface members
     private:
        static std::size_t ctr;
        // other implementation members
     };

defines a class template named Foo that among other members has a public static member function named count and a private static data member named ctr.

定义了名为 Foo 的类模板,它有一个名为 countpublic static 成员函数和一个名为 ctrprivate static 数据成员。

Each instantiation of class Foo has its own static member:

Foo 类的每个实例化有自己的 static 成员:

     // Each object shares the same Foo<int>::ctrand Foo<int>::count members
     Foo<int> fi, fi2, fi3;
     // has static members Foo<string>::ctrand Foo<string>::count
     Foo<string> fs;

Each instantiation represents a distinct type, so there is one static shared among the objects of any given instantiation. Hence, any objects of type Foo<int> share the same static member ctr. Objects of type Foo<string> share a different ctr member.

每个实例化表示截然不同的类型,所以给定实例外星人所有对象都共享一个 static 成员。因此,Foo<int> 类型的任意对象共享同一 static 成员 ctrFoo<string> 类型的对象共享另一个不同的 ctr 成员。

Using a static Member of a Class Template
使用类模板的 static 成员

As usual, we can access a static member of a class template through an object of the class type or by using the scope operator to access the member directly. Of course, when we attempt to use the static member through the class, we must refer to an actual instantiation:

通常,可以通过类类型的对象访问类模板的 static 成员,或者通过使用作用域操作符直接访问成员。当然,当试图通过类使用 static 成员的时候,必须引用实际的实例化:

     Foo<int> fi, fi2;              // instantiates Foo<int> class
     size_t ct = Foo<int>::count(); // instantiates Foo<int>::count
     ct = fi.count();               // ok: uses Foo<int>::count
     ct = fi2.count();              // ok: uses Foo<int>::count
     ct = Foo::count();             // error: which template instantiation?

Like any other member function, a static member function is instantiated only if it is used in a program.

与任意其他成员函数一样,static 成员函数只有在程序中使用时才进行实例化。

Defining a static Member
定义 static 成员

As with any other static data member, there must be a definition for the data member that appears outside the class. In the case of a class template static, the member definition must inidicate that it is for a class template:

像使用任意其他 static 数据成员一样,必须在类外部出现数据成员的定义。在类模板含有 static 成员的情况下,成员定义必须指出它是类模板的成员:

     template <class T>
     size_t Foo<T>::ctr = 0; // define and initialize ctr

A static data member is defined like any other member of a class template that is defined outside the class. It begins with the keyword template followed by the class template parameter list and the class name. In this case, the name of the static data member is prefixed by Foo<T>::, which indicates that the member belongs to the class template Foo.

static 数据成员像定义在类外部的任意其他类成员一样定义,它用关键字 template 开头,后面接着类模板形参表和类名。在这个例子中,static 数据成员的名字以 Foo<T>:: 为前缀,表示成员属于类模板 Foo。

Team LiB
Previous Section Next Section