📄 ch09.htm
字号:
<tt> short int u = 5, v = 10;</tt><tt> short int w = min(u,v);</tt><tt>}</tt></pre><p>Still, the template version of <tt>min</tt> has a clear advantage over the function: It can handle pointers and any other user-defined types. You want the benefits of a template while avoiding the unnecessary generation of specializations. How can these be avoided? A simple solution is to safely cast the arguments into one common type before invoking the function template. For example:</p><pre><tt>void no_proliferation()</tt><tt>{</tt><tt> short n = 5, m= 10;</tt><tt> int j = min( static_cast<int> (n), </tt><tt> static_cast<int> (m) ); //min<int> instantiated</tt><tt> char c = 'a', d = 'b';</tt><tt> char k = static_cast<char> (min( static_cast<int> , </tt><tt> static_cast<int> ) ); //min<int> used</tt><tt>}</tt></pre><p>This technique can also be applied to pointers: First, they have to be cast to <tt>void<b> </b>*</tt>, and then they must be cast back to their original type. When you are using pointers to polymorphic objects, pointers to derived objects are cast to pointers to a base class.</p><p>For very small templates such as <tt>min</tt>, casting the arguments into a common denominator type is not worth the trouble. Nonetheless, when nontrivial templates that contain hundreds of code lines are used, you might consider doing so.</p><h3> <a name="Heading19">Explicit Template Instantiation</a></h3><p>As was previously noted, templates are instantiated only if they are used in the program. Generally, compilers generate the necessary code for a specialization when they encounter its use in the source file. When large applications that consist of hundreds of source files have to be compiled, this can cause a significant increase in compilation time because the compiler's processing is repeatedly interrupted when it has to generate code for sporadic specializations. The recurrent interruption can occur in almost every source file because they use -- almost without exception -- template classes of the Standard Library. Consider a simple project that consists of only three source files:</p><pre><tt>//filename func1.cpp</tt><tt>#include <string></tt><tt>#include <iostream></tt><tt>using namespace std;</tt><tt>void func1()</tt><tt>{</tt><tt> string s; //generate default constructor and destructor</tt><tt> s = "hello"; //generate assignment operator for const char *</tt><tt> string s2;</tt><tt> s2 = s; // generate operator = const string&, string&</tt><tt> cout<<s2.size(); // generate string::size, ostream& operator <<(int)</tt><tt>}</tt><tt>//func1.cpp</tt><tt>//filename func2.cpp</tt><tt>#include <string></tt><tt>#include <iostream></tt><tt>using namespace std;</tt><tt>void func2()</tt><tt>{</tt><tt> string s; //generate default constructor and destructor</tt><tt>cout<<"enter a string: "<<endl; //generate ostream& operator<<(const char *)</tt><tt> cin>>s //generate istream& operator>>(string&)</tt><tt>}</tt><tt>//func2.cpp</tt><tt>//filename main.cpp</tt><tt>int main()</tt><tt>{</tt><tt> func1();</tt><tt> func2();</tt><tt> retrun 0;</tt><tt>}</tt><tt>// main.cpp</tt></pre><p>The compilation time can be reduced if all the necessary template code is instantiated all at once, thereby avoiding the repeated interruption of the compiler's processing. For this purpose, you can use an <i>explicit instantiation</i>. An explicit instantiation is indicated by the keyword <tt>template</tt> (without the <tt><></tt>), followed by a template declaration.<b> </b>Here are a few examples of explicit template instantiations:</p><pre><tt>template <class T> class A{/*..*/};</tt><tt>template<class T> void func(T&) { }</tt><tt> //filename instantiations.hpp</tt><tt>template class Vector<short>; //explicit instantiation of a class template</tt><tt>template A<int>::A<int>(); //explicit instantiation of a member function</tt><tt>template class </tt><tt> std::basic_string<char>; //explicit instantiation of a namespace member</tt><tt>template void func<int>(int&); //explicit instantiation of a function template</tt></pre><h4> Examples of Explicit Instantiations in the Standard Library</h4><p>The Standard Library defines several specializations of class templates. One example is the class template <tt>basic_string<></tt>. Two specialized versions of this class are <tt>std::string</tt> and <tt>std::wstring</tt>, which are <tt>typedef</tt>s of the specializations <tt>basic_string<char></tt> and <tt>basic_string<wchar_t></tt>, respectively. Usually, there is no need to instantiate any of these explicitly because the header <tt><string></tt> already instantiates these specializations:</p><pre><tt>#include <string> //definitions of std::string and std::wstring</tt><tt>using namespace std;</tt><tt>bool TranslateToKorean(const string& origin, </tt><tt> wstring& target ); //English / Korean dictionary</tt><tt> int main()</tt><tt> {</tt><tt> string EnglishMsg = "This program has performed an illegal operation";</tt><tt> wstring KoreanMsg;</tt><tt> TranslateToKorean(EnglishMsg, KoreanMsg);</tt><tt> }</tt></pre><h3> <a name="Heading20"> Exported Templates</a></h3><blockquote> <hr> <strong>NOTE: </strong> Exported templates are relatively new in C++; therefore, not all compilers support this feature yet. Please consult your user's manual to check whether your compiler supports it. <hr></blockquote><p>A template definition can be <tt>#included</tt> in several translation units; consequently, it can be compiled several times. As you have observed, this can considerably increase compilation and linkage time. Instead of <tt>#including</tt> a complete definition of the template, it is possible to compile the template definition only once, and use only the template's declaration in other translation units. This is very similar to the compilation of external functions and classes, in which the definition is compiled only once and then only the declarations are required.</p><p>To compile a template separately and then use its declaration, the template has to be <i>exported</i>. This is done by preceding the template's definition with the keyword <tt>export</tt>:</p><pre><tt>//filename min.cpp</tt><tt>export template < class T > T min (const T& a, const T& b)</tt><tt>{</tt><tt> return a > b ? b : a;</tt><tt>}</tt></pre><p>Now only the declaration of the template is required when it is used in other translation units. For example</p><pre><tt> //file min.c</tt><tt>template < class T > T min (const T & a, const T & b); //declaration only</tt><tt>int main()</tt><tt>{</tt><tt> int j=0, k=1;</tt><tt> in smaller = min(j,k);</tt><tt> return 0;</tt><tt>}</tt></pre><p>Inline function templates cannot be exported. If an inline template function is declared both <tt>export</tt> and <tt>inline</tt>, the <tt>export</tt> declaration has no effect and the template is only inline. Declaring a class template exported is equivalent to declaring all its non-inline member functions, static data members, and member classes exported. Templates in an unnamed namespace are not to be exported.</p><h2> <a name="Heading21">Interaction with Other Language Features</a></h2><p>The interaction of templates with other language features can sometimes yield surprising results. The following sections discuss various aspects of interaction between templates and other language features, including ambiguous interpretation of qualified template names, inheritance, and virtual member functions.</p><h3> <a name="Heading22">The typename Keyword</a></h3><p>Using qualified names in a template can cause ambiguity between a type and a non-type. For example</p><pre><tt>int N;</tt><tt>template < class T > T func()</tt><tt>{</tt><tt> T::A * N; // ambiguous: multiplication or a pointer declaration?</tt><tt>//</tt><tt>}</tt></pre><p> If <tt>T::A</tt> is a typename, the definition of <tt>func() N</tt> creates a pointer. If, on the other hand, <tt>T::A</tt> is a non-type (for example, if <tt>A</tt> is data member of type <tt>int</tt>), <tt>T::A * N</tt> is an expression statement that consists of the multiplication of the qualified member <tt>T::A</tt> by a global <tt>int N</tt>. By default, the compiler assumes that an expression such as <tt>T::A</tt> refers to a non-type. The <tt>typename</tt> keyword instructs the compiler to supersede this default interpretation and resolve the ambiguity in favor of a type name rather than a non-type. In other words, the preceding (seemingly ambiguous) statement is actually resolved as a multiplication expression, the result of which is discarded. In order to declare a pointer, the <tt>typename</tt> keyword is required:</p><pre><tt>int N;</tt><tt>template < class T > T func()</tt><tt>{</tt><tt> typename T::A * N; // N is a now pointer since T::A is a typename</tt><tt>//...</tt><tt>};</tt></pre><h3> <a name="Heading23">Inheritance Relationship of Templates</a></h3><p>A common mistake is to assume that a container of pointers or references to objects of a derived class is a container of pointers or references to a base class. For example</p><pre><tt>#include<vector></tt><tt>using namespace std;</tt><tt>class Base</tt><tt>{</tt><tt>public: virtual void f() {}</tt><tt>};</tt><tt>class Derived : public Base</tt><tt>{</tt><tt>public: void f() {}</tt><tt>};</tt><tt>void func( vector<Base*>& vb);</tt><tt>int main()</tt><tt>{</tt><tt> Derived d;</tt><tt> vector<Derived*> vd;</tt><tt> vd.push_back(&d);</tt><tt> func(vd); //error, vector<Derived*>& is not a vector<Base*></tt><tt>}</tt></pre><p>Although the is-a relationship exists between the classes <tt>Derived</tt> and <tt>Base</tt>, there is no such relationship between specializations of the same class template that contain pointers or references to related objects.</p><h3> <a name="Heading24">Virtual Member Functions</a></h3><p>A member function template should not be virtual. However, ordinary member functions in a class template <i>can</i> be virtual. For example</p><pre><tt>template <class T> class A</tt><tt>{</tt><tt>public:</tt><tt> template <class S> virtual void f(S); //error</tt><tt> virtual int g(); // OK</tt><tt>};</tt></pre><p>A specialization of a member function template does not override a virtual function that is defined in a base class. For example</p><pre><tt>class Base</tt><tt>{</tt><tt>public:</tt><tt> virtual void f(char);</tt><tt>};</tt><tt>class Derived : public Base</tt><tt>{</tt><tt>public:</tt><tt> template <class T> void f(T); //does not override B::f(int)</tt><tt>};</tt></pre><h3> <a name="Heading25">Pointers to Members of a Class Template</a></h3><p>Pointers to class members can take the address of a specialized member function of a class template. As with ordinary classes, pointers to members cannot take the address of a static member function. In the following example, the specialization <tt>std::vector<int></tt> is used:</p><pre><tt>#include<vector></tt><tt>using namespace std;</tt><tt> // a typedef is used to hide the unwieldy syntax</tt><tt>typedef void (vector< int >::*pmv) (size_t);</tt><tt>void func()</tt><tt>{</tt><tt> pmv reserve_ptr = &vector< int >::reserve;</tt><tt> //...use reserve_ptr</tt><tt>}</tt></pre><h2> <a name="Heading26">Conclusions</a></h2><p>Templates simplify and streamline the implementation of generic containers and functions. The benefits of templates have allured software vendors, who are now migrating from plain object-oriented frameworks to object-oriented generic frameworks. However, parameterized types are not unique to C++. Back in 1983, Ada introduced generic packages, which were roughly equivalent to class templates. Other languages implemented similar mechanisms of automated code generation from a skeletal user-written code.</p><p>The two main template categories in C++ are class templates and function templates. A class template encapsulates parameterized data members and function members. Function templates are a means of implementing generic algorithms. The traditional methods of implementing generic algorithms in pre-template C++ were rather limited, unsafe, and inefficient compared to templates. An important aspect of templates is the support of object semantics.</p><p>C++ enables programmers to control the instantiation of templates by explicitly instantiating them. It is possible to instantiate an entire class, a certain member function of a class, or a particular specialization of a function template. An explicit instantiation of a template is indicated by the keyword <tt>template</tt>, without angular brackets that are followed by the template declaration. Explicit specializations of a class template are always required. For function templates, the compiler usually deduces the specialization from the type of the arguments. It is possible to define partial specialization of a class template that overrides the primary class template for a set of types. This feature is most useful for modifying the behavior of a class template that manipulates pointers. A partial specialization is indicated by a secondary list of parameters following the name of the template. An explicit specialization enables the programmer to override the automatic instantiation of a class template for a certain type. An explicit specialization is indicated by the keyword <tt>template</tt>, followed by empty angular brackets and a list of type arguments after the template's name.</p><p>Templates and operator overloading are the building blocks of generic programming. The Standard Template Library is an exemplary framework of generic programming, as you will see in the next chapter.</p><CENTER><P><HR> <A HREF="/publishers/que/series/professional/0789720221/index.htm"><img src="/publishers/que/series/professional/0789720221/button/contents.gif" WIDTH="128"HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR><BR><BR><p></P><P>© <A HREF="/publishers/que/series/professional/0789720221/copy.htm">Copyright 1999</A>, Macmillan Computer Publishing. Allrights reserved.</p></CENTER></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -