⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 4 页
字号:
<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&lt;const char *&gt;</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 &lt;class T&gt; class Vector;</tt>
<tt>template &lt;class T&gt; bool operator== (const Vector&lt;T&gt;&amp; v1, const Vector&lt;T&gt;&amp; v2);</tt>
<tt>template &lt;class T&gt; class Vector   </tt>
<tt>{</tt>
<tt>//...</tt>
<tt>public:</tt>
<tt>  friend bool operator==&lt;T&gt; (const Vector&lt;T&gt;&amp; v1, </tt>
<tt>                             const Vector&lt;T&gt;&amp; v2); // primary</tt>
<tt>  friend bool operator== ( //specialized version </tt>
<tt>                                 const Vector&lt;const char *&gt;&amp; v1,</tt>
<tt>                                 const Vector&lt;const char *&gt;&amp; 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 &lt;cstring&gt; //needed for strcmp</tt>
<tt>using namespace std;</tt>
<tt>template &lt;&gt; bool operator== (</tt>
<tt>                                            const Vector&lt;const char *&gt;&amp; v1,</tt>
<tt>                                            const Vector&lt;const char *&gt;&amp; v2 )</tt>
<tt>{</tt>
<tt>  if (v1.size() != v2.size())  //as before</tt>
<tt>    return false;</tt>
<tt>  for (size_t i = 0; i&lt;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)&lt;(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>&lt;stdlib.h&gt;</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 &lt; (const Object&amp;) const; //polymorphic behavior</tt>
<tt>  //..other members</tt>
<tt>};</tt>
<tt>const Object&amp; min(const Object &amp;x, const Object&amp; y)</tt>
<tt>{</tt>
<tt>  return  x.operator&lt;(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 &lt;class T&gt; T max( T t1, T t2)</tt>
<tt>{</tt>
<tt>  return (t1 &gt; 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 &lt;string&gt;</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 &lt;class T&gt; T max( T t1, T t2)</tt>
<tt>{</tt>
<tt>  return (t1 &gt; t2) ? t1 : t2;</tt>
<tt>}</tt>
<tt>int max (int i, int j)</tt>
<tt>{</tt>
<tt>  return (i &gt; 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&lt;class T, long size&gt; class Array</tt>
<tt>{ /* ... */ };</tt>
<tt>void func()</tt>
<tt>{</tt>
<tt>Array&lt;char, 2*512&gt; a;</tt>
<tt>Array&lt;char, 1024&gt; 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&lt;class T, int(*error_code_fct)()&gt; class Buffer</tt>
<tt>{ /* ... */ };</tt>
<tt>int error_handler();</tt>
<tt>int another_error_handler();</tt>
<tt>void func()</tt>
<tt>{</tt>
<tt>  Buffer&lt;int, &amp;error_handler&gt; b1;</tt>
<tt>  Buffer&lt;int, &amp;another_error_handler&gt; b2;</tt>
<tt>  Buffer&lt;int, &amp;another_error_handler&gt; b3;</tt>
<tt>  Buffer&lt;unsigned int, &amp;another_error_handler&gt; 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&lt;int, &amp;error_handler&gt; buff1;</tt>
<tt>  Buffer&lt;long, &amp;error_handler&gt; 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 &lt; class T &gt; T min(T f, T s)</tt>
<tt>{</tt>
<tt>  return f &lt; 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&lt;int&gt; instantiated</tt>
<tt>  char c = 'a', d = 'b';</tt>
<tt>  char k = min(c,d);     //min&lt;char&gt; instantiated</tt>
<tt>  short int u = 5, v = 10;</tt>
<tt>  short int w = min(u,v);    // min&lt;short int&gt; 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 &lt; 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 + -