16.3. Template Compilation Models16.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++ 为编译模板代码定义了两种模型。在两种模型中,构造程序的方式很大程度上是相同的:类定义和函数声明放在头文件中,而函数定义和成员定义放在源文件中。两种模型的不同在于,编译器怎样使用来自源文件的定义。如本书所述,所有编译器都支持第一种模型,称为“包含”模型,只有一些编译器支持第二种模型,“分别编译”模型。
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 不在类模板本身指定,而是只在被导出的特定成员定义上指定。导出成员函数的定义不必在使用成员时可见。任意非导出成员的定义必须像在包含模型中一样对待:定义应放在定义类模板的头文件中。
|