Team LiB
Previous Section Next Section

16.3. Template Compilation Models

16.3. 模板编译模型

When the compiler sees a template definition, it does not generate code immediately. The compiler produces type-specific instances of the template only when it sees a use of the template, such as when a function template is called or an object of a class template is defined.

当编译器看到模板定义的时候,它不立即产生代码。只有在看到用到模板时,如调用了函数模板或调用了类模板的对象的时候,编译器才产生特定类型的模板实例。

Ordinarily, when we call a function, the compiler needs to see only a declaration for the function. Similarly, when we define an object of class type, the class definition must be available, but the definitions of the member functions need not be present. As a result, we put class definitions and function declarations in header files and definitions of ordinary and class-member functions in source files.

一般而言,当调用函数的时候,编译器只需要看到函数的声明。类似地,定义类类型的对象时,类定义必须可用,但成员函数的定义不是必须存在的。因此,应该将类定义和函数声明放在头文件中,而普通函数和类成员函数的定义放在源文件中。

Templates are different: To generate an instantiation, the compiler must have access to the source code that defines the template. When we call a function template or a member function of a class template, the compiler needs the function definition. It needs the code we normally put in the source files.

模板则不同:要进行实例化,编译器必须能够访问定义模板的源代码。当调用函数模板或类模板的成员函数的时候,编译器需要函数定义,需要那些通常放在源文件中的代码。

Standard C++ defines two models for compiling template code. In each of these models, we structure our programs in largely the same way: Class definitions and function declarations go in header files, and function and member definitions go in source files. The two models differ in how the definitions from the source files are made available to the compiler. As of this writing, all compilers support the first model, known as the "inclusion" model; only some compilers support the second, "separate compilation" model.

标准 C++ 为编译模板代码定义了两种模型。在两种模型中,构造程序的方式很大程度上是相同的:类定义和函数声明放在头文件中,而函数定义和成员定义放在源文件中。两种模型的不同在于,编译器怎样使用来自源文件的定义。如本书所述,所有编译器都支持第一种模型,称为“包含”模型,只有一些编译器支持第二种模型,“分别编译”模型。

To compile code that uses your own class and function templates, you must consult your compiler's user's guide to see how your compiler handles instantiation.

要编译使用自己的类模板和函数模板的代码,必须查阅编译器的用户指南,看看编译器怎样处理实例化。



Inclusion Compilation Model

包含编译模型

In the inclusion compilation model, the compiler must see the definition for any template that is used. Typically, we make the definitions available by adding a #include directive to the headers that declare function or class templates. That #include brings in the source file(s) that contain the associated definitions:

包含编译模型中,编译器必须看到用到的所有模板的定义。一般而言,可以通过在声明函数模板或类模板的头文件中添加一条 #include 指示使定义可用,该 #include 引入了包含相关定义的源文件:

     // header file utlities.h
     #ifndef UTLITIES_H // header gaurd (Section 2.9.2, p. 69)
     #define UTLITIES_H
     template <class T> int compare(const T&, const T&);
     // other declarations

     #include "utilities.cc" // get the definitions for compare etc.
     #endif

     // implemenatation file utlities.cc
     template <class T> int compare(const T &v1, const T &v2)
     {
         if (v1 < v2) return -1;
         if (v2 < v1) return 1;
         return 0;
     }
     // other definitions

This strategy lets us maintain the separation of header files and implementation files but ensures that the compiler will see both files when compiling code that uses the templates.

这一策略使我们能够保持头文件和实现文件的分享,但是需要保证编译器在编译使用模板的代码时能看到两种文件。

Some, especially older, compilers that use the inclusion model may generate multiple instantiations. If two or more separately compiled source files use the same template, these compilers will generate an instantiation for the template in each file. Ordinarily, this approach implies that a given template will be instantiated more than once. At link time, or during a prelink phase, the compiler selects one instantiation, discarding the others. In such cases, compile-time performance can be significantly degraded if there are a lot of files that instantiate the same template. This compile-time degradation is unlikely to be a problem on modern computers for many applications. However, in the context of large systems, the compile-time hit may become important.

某些使用包含模型的编译器,特别是较老的编译器,可以产生多个实例。如果两个或多个单独编译的源文件使用同一模板,这些编译器将为每个文件中的模板产生一个实例。通常,这种方法意味着给定模板将实例化超过一次。在链接的时候,或者在预链接阶段,编译器会选择一个实例化而丢弃其他的。在这种情况下,如果有许多实例化同一模板的文件,编译时性能会显著降低。对许多应用程序而言,这种编译时性能降低不大可能在现代计算机上成为问题,但是,在大系统环境中,编译时选择问题可能变得非常重要。

Such compilers often support mechanisms that avoid the compile-time overhead implicit in multiple instantiations of the same template. The way compilers optimize compile-time performance varies from one compiler to the next. If compile time for programs using templates is too burdensome, consult your compiler's user's guide to see what support your compiler offers to avoid redundant instantiations.

这种编译器通常支持某些机制,避免同一模板的多个实例化中隐含的编译进开销。编译器优化编译时性能的方法各不相同。如果使用模板的程序的编译时间难于承担,请查阅编译器的用户指南,看看你的编译器能提供什么支持以避免多余的实例化。

Separate Compilation Model

分别编译模型

In the separate compilation model, the compiler keeps track of the associated template definitions for us. However, we must tell the compiler to remember a given template definition. We use the export keyword to do so.

分别编译模型中,编译器会为我们跟踪相关的模板定义。但是,我们必须让编译器知道要记住给定的模板定义,可以使用 export 关键字来做这件事。

The export keyword indicates that a given definition might be needed to generate instantiations in other files. A template may be defined as exported only once in a program. The compiler figures out how to locate the template definition when it needs to generate these instantiations. The export keyword need not appear on the template declaration.

export 关键字能够指明给定的定义可能会需要在其他文件中产生实例化。在一个程序中,一个模板只能定义为导出一次。编译器在需要产生这些实例化时计算出怎样定位模板定义。export 关键字不必在模板声明中出现。

Ordinarily, we indicate that a function template is exported as part of its definition. We do so by including the keyword export before the template keyword:

一般我们在函数模板的定义中指明函数模板为导出的,这是通过在关键字 template 之前包含 export 关键字而实现的:

     // the template definition goes in a separately-compiled source file
     export template <typename Type>
     Type sum(Type t1, Type t2) /* ...*/

The declaration for this function template, should, as usual, be put in a header. The declaration must not specify export.

这个函数模板的声明像通常一样应放在头文件中,声明不必指定 export

Using export on a class template is a bit more complicated. As usual, the class declaration must go in a header file. The class body in the header should not use the export keyword. If we used export in the header, then that header could be used by only one source file in the program.

对类模板使用 export 更复杂一些。通常,类声明必须放在头文件中,头文件中的类定义体不应该使用关键字 export,如果在头文件中使用了 export,则该头文件只能被程序中的一个源文件使用。

Instead, we export the class in the class implementation file:

相反,应该在类的实现文件中使用 export

     // class template header goes in shared header file
     template <class Type> class Queue { ... };
     // Queue.ccimplementation file declares Queue as exported
     export template <class Type> class Queue;
     #include "Queue.h"
     // Queue member definitions

The members of an exported class are automatically declared as exported. It is also possible to declare individual members of a class template as exported. In this case, the keyword export is not specified on the class template itself. It is specified only on the specific member definitions to be exported. The definition of exported member functions need not be visible when the member is used. The definitions of any nonexported member must be treated as in the inclusion model: The definition should be placed inside the header that defines the class template.

导出类的成员将自动声明为导出的。也可以将类模板的个别成员声明为导出的,在这种情况下,关键字 export 不在类模板本身指定,而是只在被导出的特定成员定义上指定。导出成员函数的定义不必在使用成员时可见。任意非导出成员的定义必须像在包含模型中一样对待:定义应放在定义类模板的头文件中。

Exercises Section 16.3

Exercise 16.27:

Determine which compilation model your compiler uses. Write and call a function template to find the median value in a vector that holds objects of unknown type. (Note: The median is a value such that half the elements are larger than the median, and half are smaller.) Structure your program in the normal way: The function definition should go in one file, a declaration for it in a header, which the code that defines and uses the function template should include.

确定你的编译器使用的是哪种编译模型。编写并调用函数模板,在保存未知类型对象的 vector 中查找中间值。(注:中间值是这样一个值,一半元素比它大,一半元素比它小。)用常规方式构造你的程序:函数定义应放在一个文件中,它的声明放在一个头文件中,定义和使用函数模板的代码应包含该头文件。

Exercise 16.28:

Where would you place the definitions for the member functions and static data members of your class templates if the compiler you use supports the separation compilation model? Explain why.

如果所用的编译器支持分别编译模型,将类模板的成员函数和 static 数据成员的定义放在哪里?为什么?

Exercise 16.29:

Where would you put those template member definitions if your compiler uses the inclusion model? Explain why.

如果你的编译器使用包含模型,将那些模板成员定义放在哪里?为什么?


Caution: Name Lookup in Class Templates

警告:类模板中的名字查找

Compiling templates is a surprisingly difficult task. Fortunately, it is a task handled by compiler writers. Unfortunately, some of that complexity is pushed onto users of templates: Templates contain two kinds of names:

编译模板是异常困难的工作。幸好,它是由编译器作者处理的任务。不幸的是,某些复杂性被推到模板用户的身上:模板包含两种名字:

  1. Those that do not depend on a template parameter

    独立于模板形参的那些名字

  2. Those that do depend on a template parameter

    依赖于模板形参的那些名字

It is up to the template designer to ensure that all names that do not depend on a template parameter are defined in the same scope as the template itself.

设计者的责任是,保证所有不依赖于模板形参的名字在模板本身的作用域中定义。

It is up to users of a template to ensure that declarations for all functions, types, and operators associated with the types used to instantiate the template are visible. This responsibility means that the user must ensure that these declarations are visible when a member of a class template or a function template is instantiated.

模板用户的责任是,保证与用来实例化模板的类型相关的所有函数、类型和操作符的声明可见。这个责任意味着,在实例化类模板的成员或函数模板的时候,用户必须保证这些声明是可见的。

Both of these requirements are easily satisfied by well-structured programs that make appropriate use of headers. Authors of templates should provide a header that contains declarations for all the names used in the class template or in the definitions of its members. Before defining a template on a particular type or using a member of that template, the user must ensure that the header for the template type and the header that defines the type used as the element type are included.

适当使用头文件的结构良好的程序都容易满足这两个要求。模板的作者应提供头文件,该头文件包含在类模板或在其成员定义中使用的所有名字的声明。在用特定类型定义模板或者使用该模板的成员之前,用户必须保证包含了模板类型的头文件,以及定义用作成员类型的类型的头文件。


Team LiB
Previous Section Next Section