📄 index.html
字号:
<blockquote>
<hr>
<strong>NOTE: </strong> Whether identical string literals are treated as distinct
objects is implementation-dependent. Some implementations might store the constants
<tt>msg1</tt> and <tt>msg2</tt> at the same memory address (on such implementations,
the expression <tt>bool equal = (v1 == v2);</tt> yields true). However, the
discussion here assumes that <tt>msg1</tt> and <tt>msg2</tt> are stored in two
distinct memory addresses.
<hr>
</blockquote>
<p>Although <tt>v1</tt> and <tt>v2</tt> have the same number of elements and their
elements hold the same string value, operator <tt>==</tt> returns <tt>false</tt>
because it compares the addresses of the strings rather than the strings themselves.
You can alter this behavior by defining a specialized version of operator <tt>==</tt>
for type <tt>const char *</tt> exclusively, which compares the strings rather
than their addresses. The compiler picks the specialized version only when objects
of type <tt>Vector<const char *></tt> are compared. Otherwise, it uses
the primary version of operator <tt>==</tt>. It is not necessary to add the
declaration of the specialized friend operator <tt>==</tt> in the declaration
of the template class <tt>Vector</tt>. However, it is still recommended that
you do so in order to document the existence of a specialized operator <tt>==</tt>.
For example</p>
<pre>
<tt>template <class T> class Vector;</tt>
<tt>template <class T> bool operator== (const Vector<T>& v1, const Vector<T>& v2);</tt>
<tt>template <class T> class Vector </tt>
<tt>{</tt>
<tt>//...</tt>
<tt>public:</tt>
<tt> friend bool operator==<T> (const Vector<T>& v1, </tt>
<tt> const Vector<T>& v2); // primary</tt>
<tt> friend bool operator== ( //specialized version </tt>
<tt> const Vector<const char *>& v1,</tt>
<tt> const Vector<const char *>& v2);</tt>
<tt>};</tt>
</pre>
<p>The definition of the specialized function must appear after the generic version.
Therefore, you place it at the same header file, right after the generic version.
Following is the specialized version:</p>
<pre>
<tt>//appended to vector.hpp</tt>
<tt>#include <cstring> //needed for strcmp</tt>
<tt>using namespace std;</tt>
<tt>template <> bool operator== (</tt>
<tt> const Vector<const char *>& v1,</tt>
<tt> const Vector<const char *>& v2 )</tt>
<tt>{</tt>
<tt> if (v1.size() != v2.size()) //as before</tt>
<tt> return false;</tt>
<tt> for (size_t i = 0; i<v1.size(); i++)</tt>
<tt> {</tt>
<tt> if (strcmp(v1[i], v2[i]) != 0) //compare string values</tt>
<tt> return false;</tt>
<tt> }</tt>
<tt> return true;</tt>
<tt>}</tt>
</pre>
<p>Here again, the empty angular brackets that follow the keyword <tt>template</tt>
indicate a specialized version that overrides a previously defined generic version.
The compiler now uses the specialized form of operator <tt>==</tt> to compare
<tt>v1</tt> and <tt>v2</tt>; as expected, the result is now <tt>true</tt>.</p>
<p>Specialized functions operate in a way that resembles the behavior of virtual
member functions in a derived class. In both cases, the actual function that
is being called depends on the type. However, the virtual dispatch mechanism
relies on the dynamic type of the object, whereas template function specializations
are resolved statically.</p>
<h2> <a name="Heading12">Function Templates</a></h2>
<p>Many algorithms perform a sequence of identical operations, regardless of the
data type they manipulate. <tt>min</tt> and <tt>max</tt>, <tt>array sort</tt>,
and <tt>swap</tt> are examples of such type-independent algorithms. In the pre-template
era, programmers had to use alternative techniques to implement generic algorithms:--
macros, <tt>void</tt> pointers, and a common root base -- all of which incurred
significant drawbacks. This section exemplifies the drawbacks of these techniques
and then demonstrates how function templates are used in implementing generic
algorithms. </p>
<h3> <a name="Heading13">Function-Like Macros</a></h3>
<p>Function-like macros were the predominant form of implementing generic algorithms
in C. For example</p>
<pre>
<tt>#define min(x,y) ((x)<(y))?(x):(y)</tt>
<tt>void f()</tt>
<tt>{</tt>
<tt> double dlower = min(5.5, 5.4); </tt>
<tt> int ilower = min(sizeof(double), sizeof(int)); </tt>
<tt> char clower = min('a', 'b'); </tt>
<tt>}</tt>
</pre>
<p>The C Standard library defines various function-like macros. To some extent,
they can be used effectively because they avoid the overhead that is associated
with a full-blown function call. However, macros have significant drawbacks
as well. The preprocessor macro expansion is a simple text substitution, with
very limited awareness of scope rules and type-checking. Furthermore, macros
are notoriously hard to debug because the compiler scans a source file that
might look very different from the original file. For this reason, compiler
diagnostics can refer to code that the original source file does not contain.
Macros can easily bloat the size of a program because every macro call is expanded
inline. When large macros are called repeatedly, the result can be a dramatic
increase in the size of the program. In spite of the syntactic similarity, macros
are semantically very different from functions -- they have no linkage, address,
or storage type. For these reasons, use macros sparingly -- if at all.</p>
<h3> <a name="Heading14">void Pointers</a></h3>
<p>An alternative approach to macros is the use of a generic pointer, <tt>void
*</tt>, which can hold the address of any data type. The C standard library
defined two generic functions that rely on this feature: <tt>qsort</tt> and
<tt>bsearch</tt>. <tt>qsort</tt> is declared in the header <tt><stdlib.h></tt>
as follows:</p>
<pre>
<tt>void qsort( void *,</tt>
<tt> size_t,</tt>
<tt> size_t,</tt>
<tt> int (*) (const void *, const void *)</tt>
<tt> );</tt>
</pre>
<p>The type-unawareness of these generic functions is achieved by using <tt>void</tt>
pointers and an abstract, user-defined comparison function. Still, there are
noticeable limitations to this technique. <tt>void</tt> pointers are not type-safe,
and the repeated function callbacks impose runtime overhead that, in most cases,
cannot be avoided by inlining.</p>
<h3> <a name="Heading15">A Common Root Base</a></h3>
<p>In some other object-oriented languages, every object is ultimately derived
from a common base class (this design approach and its deficiencies in C++ are
described in further detail in Chapter 5, "Object-Oriented Programming and Design").
Generic algorithms can rely on this feature. For example</p>
<pre>
<tt>// pseudo C++ code</tt>
<tt>class Object // a common root class</tt>
<tt>{</tt>
<tt>public:</tt>
<tt> virtual bool operator < (const Object&) const; //polymorphic behavior</tt>
<tt> //..other members</tt>
<tt>};</tt>
<tt>const Object& min(const Object &x, const Object& y)</tt>
<tt>{</tt>
<tt> return x.operator<(y) ? x : y; //x and y can be objects of any class type</tt>
<tt>}</tt>
</pre>
<p>Imitating this approach in C++ is not as useful as it is in other languages,
though. C++ does not force a common root class. Therefore, it is the programmer's
-- not the implementation's -- responsibility to ensure that every class is
derived from a common base. Worse yet, the common root class is not standardized.
Thus, such algorithms are not portable. In addition, these algorithms cannot
handle fundamental types because they are limited to class objects exclusively.
Finally, the extensive use of runtime type checking imposes an unacceptable
performance on general-purpose algorithms.</p>
<h2> </h2>
<p>Function templates are free from all these drawbacks. They are type-safe, they
can be inlined by the compiler to boost performance, and -- most importantly
-- they are applicable to fundamental types and user-defined types alike.</p>
<p>A function template declaration contains the keyword <tt>template</tt>, followed
by a list of template parameters and a function declaration. As opposed to ordinary
functions, which are usually declared in one translation unit and defined in
another, the definition of a function template follows its declaration. For
example</p>
<pre>
<tt>template <class T> T max( T t1, T t2)</tt>
<tt>{</tt>
<tt> return (t1 > t2) ? t1 : t2;</tt>
<tt>}</tt>
</pre>
<p>Unlike class templates, function template parameters are implicitly deduced
from the type of their arguments. </p>
<p>In the following example, the compiler instantiates three distinct specializations
of <tt>swap</tt>, according to the type of arguments used in each invocation:</p>
<pre>
<tt>#include <string></tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> int i = 0, j = 8;</tt>
<tt> char c = 'a', d = 'z';</tt>
<tt> string s1 = "first", s2 = "second";</tt>
<tt> int nmax = max(i, j); // int max (int, int);</tt>
<tt> char cmax = max(c, d); // char max (char, char);</tt>
<tt> string smax = max(s1, s2); // string max (string, string);</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p>It is possible to define several function templates with the same name (that
is, to overload it) or to combine ordinary functions with function templates
that have the same name. For example</p>
<pre>
<tt>template <class T> T max( T t1, T t2)</tt>
<tt>{</tt>
<tt> return (t1 > t2) ? t1 : t2;</tt>
<tt>}</tt>
<tt>int max (int i, int j)</tt>
<tt>{</tt>
<tt> return (i > j) ? i : j;</tt>
<tt>}</tt>
</pre>
<p>The compiler does not generate an <tt>int</tt> version of <tt>max()</tt>. Instead,
it invokes the function <tt>int max (int, int)</tt> when <tt>max()</tt> is called
with arguments of type <tt>int</tt>.</p>
<h2> <a name="Heading16">Performance Considerations</a></h2>
<p>C++ provides several facilities for controlling the instantiation of templates,
including explicit instantiation of templates and exported templates. The following
sections demonstrate how to use these features, as well as other techniques
to enhance performance. </p>
<h3> <a name="Heading17">Type Equivalence</a></h3>
<p>Two templates are equivalent (that is, they refer to the same template id)
when all the following conditions hold:</p>
<ul>
<li>
<p> <b>Name equivalence</b> -- The names of the templates are identical and
they refer to the same template.</p>
</li>
<p></p>
<li>
<p> <b>Argument Equivalence</b> -- The type arguments of the templates are
the same.</p>
</li>
<p></p>
<li>
<p> <b>Identical non-Type Arguments</b> -- The non-type arguments of integral
or enumeration type of both templates have identical values, and their non-type
arguments of pointer or reference type refer to the same external object
or function.</p>
</li>
<p></p>
<li>
<p> <b>Template-template Arguments</b> -- The template-template arguments
of both templates refer to the same template.</p>
</li>
</ul>
<p></p>
<p>Following are some examples:</p>
<pre>
<tt>template<class T, long size> class Array</tt>
<tt>{ /* ... */ };</tt>
<tt>void func()</tt>
<tt>{</tt>
<tt>Array<char, 2*512> a;</tt>
<tt>Array<char, 1024> b;</tt>
<tt>}</tt>
</pre>
<p>The compiler evaluates constant expressions such as <tt>2*512</tt>, so templates
<tt>a</tt> and <tt>b</tt> are of the same type. Following is another example:</p>
<pre>
<tt>template<class T, int(*error_code_fct)()> class Buffer</tt>
<tt>{ /* ... */ };</tt>
<tt>int error_handler();</tt>
<tt>int another_error_handler();</tt>
<tt>void func()</tt>
<tt>{</tt>
<tt> Buffer<int, &error_handler> b1;</tt>
<tt> Buffer<int, &another_error_handler> b2;</tt>
<tt> Buffer<int, &another_error_handler> b3;</tt>
<tt> Buffer<unsigned int, &another_error_handler> b4;</tt>
<tt>}</tt>
</pre>
<p><tt>b2</tt> and <tt>b3</tt> are of the same type because they are instances
of the same template and they take the same type and non-type arguments. Conversely,
<tt>b1</tt> and <tt>b4</tt> are of distinct types.</p>
<p>The function <tt>func()</tt> instantiated three distinct specializations from
the same class template: one for <tt>b1</tt>, a second for <tt>b2</tt> and <tt>b3</tt>,
and a third for <tt>b4</tt>. Unlike ordinary function overloading, the compiler
generates a distinct class from the template for every unique type. On machines
that have the same underlying representation for <tt>long</tt> and <tt>int</tt>
types, the following code still results in the instantiation of two distinct
templates:</p>
<pre>
<tt>void too_prolific()</tt>
<tt>{</tt>
<tt> Buffer<int, &error_handler> buff1;</tt>
<tt> Buffer<long, &error_handler> buff2;</tt>
<tt>}</tt>
</pre>
<p>Similarly, <tt>char</tt> and <tt>unsigned char</tt> are distinct types, even
on machines that treat <tt>char</tt> as <tt>unsigned</tt> by default. Programmers
who are accustomed to the lenient matching rules of ordinary function overloading
are not always aware of the potential code bloat that can result from using
templates with very similar yet distinct types.</p>
<h3> <a name="Heading18">Avoiding Unnecessary Instantiations</a></h3>
<p>In the following example, three distinct copies of the template <tt>min</tt>
are generated -- one for each type that is used:</p>
<pre>
<tt>template < class T > T min(T f, T s)</tt>
<tt>{</tt>
<tt> return f < s? f: s;</tt>
<tt>}</tt>
<tt>void use_min()</tt>
<tt>{</tt>
<tt> int n = 5, m= 10;</tt>
<tt> int j = min(n,m); //min<int> instantiated</tt>
<tt> char c = 'a', d = 'b';</tt>
<tt> char k = min(c,d); //min<char> instantiated</tt>
<tt> short int u = 5, v = 10;</tt>
<tt> short int w = min(u,v); // min<short int> instantiated</tt>
<tt>}</tt>
</pre>
<p>On the other hand, an ordinary function avoids the automatic generation of
redundant specializations:</p>
<pre>
<tt>int min(int f, int s)</tt>
<tt>{</tt>
<tt> return f < s? f: s;</tt>
<tt>}</tt>
<tt>void use_min()</tt>
<tt>{</tt>
<tt>// all three invocation of min</tt>
<tt>// use int min (int, int);</tt>
<tt> int n = 5, m= 10;</tt>
<tt> int j = min(n,m);</tt>
<tt> char c = 'a', d = 'b';</tt>
<tt> char k = min(c,d); </tt>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -