<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>舍利子的博客</title>
  
  <subtitle>心事无人诉，苦、苦、苦！</subtitle>
  <link href="https://kettycode.github.io/atom.xml" rel="self"/>
  
  <link href="https://kettycode.github.io/"/>
  <updated>2024-03-07T09:48:00.130Z</updated>
  <id>https://kettycode.github.io/</id>
  
  <author>
    <name>ketty</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>cahr 和 wchar_t</title>
    <link href="https://kettycode.github.io/2024/02/19/cpp/tip/char/"/>
    <id>https://kettycode.github.io/2024/02/19/cpp/tip/char/</id>
    <published>2024-02-19T04:59:59.000Z</published>
    <updated>2024-03-07T09:48:00.130Z</updated>
    
    <content type="html"><![CDATA[<h1 id="char和wchar-t"><a href="#char和wchar-t" class="headerlink" title="char和wchar_t"></a>char和wchar_t</h1><h2 id="char-和-wchar-t-都是-C-中的字符类型，但它们之间有一些区别。char-是一个字节（8-位）长，而-wchar-t-是两个字节（16-位）或四个字节（32-位）长，具体取决于编译器和操作系统。因此，wchar-t-可以表示更多的字符，包括-Unicode-字符。"><a href="#char-和-wchar-t-都是-C-中的字符类型，但它们之间有一些区别。char-是一个字节（8-位）长，而-wchar-t-是两个字节（16-位）或四个字节（32-位）长，具体取决于编译器和操作系统。因此，wchar-t-可以表示更多的字符，包括-Unicode-字符。" class="headerlink" title="char 和 wchar_t 都是 C++ 中的字符类型，但它们之间有一些区别。char 是一个字节（8 位）长，而 wchar_t 是两个字节（16 位）或四个字节（32 位）长，具体取决于编译器和操作系统。因此，wchar_t 可以表示更多的字符，包括 Unicode 字符。"></a>char 和 wchar_t 都是 C++ 中的字符类型，但它们之间有一些区别。<br>char 是一个字节（8 位）长，而 wchar_t 是两个字节（16 位）或四个字节（32 位）长，具体取决于编译器和操作系统。<br>因此，wchar_t 可以表示更多的字符，包括 Unicode 字符。</h2><h2 id="在-Windows-操作系统中，许多-API-函数都使用-wchar-t-类型的字符串参数。如果需要处理-Unicode-字符或调用这些函数，则应使用-wchar-t-类型。否则，可以使用-char-类型。"><a href="#在-Windows-操作系统中，许多-API-函数都使用-wchar-t-类型的字符串参数。如果需要处理-Unicode-字符或调用这些函数，则应使用-wchar-t-类型。否则，可以使用-char-类型。" class="headerlink" title="在 Windows 操作系统中，许多 API 函数都使用 wchar_t 类型的字符串参数。如果需要处理 Unicode 字符或调用这些函数，则应使用 wchar_t 类型。否则，可以使用 char 类型。"></a>在 Windows 操作系统中，许多 API 函数都使用 wchar_t 类型的字符串参数。如果需要处理 Unicode 字符或调用这些函数，则应使用 wchar_t 类型。否则，可以使用 char 类型。</h2><h2 id="为了使程序适配多语言场景，不建议在代码中使用char或wchar-t，可直接使用TCHAR类型定义字符（串），需要包含对应头文件（在包含了Windows-h时则不需要）。TCHAR在源码中的定义如下："><a href="#为了使程序适配多语言场景，不建议在代码中使用char或wchar-t，可直接使用TCHAR类型定义字符（串），需要包含对应头文件（在包含了Windows-h时则不需要）。TCHAR在源码中的定义如下：" class="headerlink" title="为了使程序适配多语言场景，不建议在代码中使用char或wchar_t，可直接使用TCHAR类型定义字符（串），需要包含对应头文件&lt;TCHAR.H&gt;（在包含了Windows.h时则不需要）。TCHAR在源码中的定义如下："></a>为了使程序适配多语言场景，不建议在代码中使用char或wchar_t，可直接使用TCHAR类型定义字符（串），需要包含对应头文件&lt;TCHAR.H&gt;（在包含了Windows.h时则不需要）。TCHAR在源码中的定义如下：</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _UNICODE</span></span><br><span class="line">     <span class="keyword">typedef</span> <span class="type">wchar_t</span> TCHAR;</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">    <span class="keyword">typedef</span> <span class="type">char</span> TCHAR;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><h2 id="同理，在涉及到字符串操作的接口时，也推荐使用支持多语言场景的接口-tcscpy、-tcslen、-tcscat，而不是strcpy-s、strlen-s、strcat-s或wcscpy、wcslen、wcscat。头文件中有很多类似的宏定义。"><a href="#同理，在涉及到字符串操作的接口时，也推荐使用支持多语言场景的接口-tcscpy、-tcslen、-tcscat，而不是strcpy-s、strlen-s、strcat-s或wcscpy、wcslen、wcscat。头文件中有很多类似的宏定义。" class="headerlink" title="同理，在涉及到字符串操作的接口时，也推荐使用支持多语言场景的接口_tcscpy、_tcslen、_tcscat，而不是strcpy_s、strlen_s、strcat_s或wcscpy、wcslen、wcscat。头文件&lt;TCHAR.H&gt;中有很多类似的宏定义。"></a>同理，在涉及到字符串操作的接口时，也推荐使用支持多语言场景的接口_tcscpy、_tcslen、_tcscat，而不是strcpy_s、strlen_s、strcat_s或wcscpy、wcslen、wcscat。头文件&lt;TCHAR.H&gt;中有很多类似的宏定义。</h2><h1 id="前缀L-和-前缀-T"><a href="#前缀L-和-前缀-T" class="headerlink" title="前缀L 和 前缀_T"></a>前缀L 和 前缀_T</h1><h2 id="通常，我们使用双引号标识ANSI字符串，比如：”I’m-an-example-“-这种类型的字符串，每个字符占用一个字节。当我们想要标识UNICODE字符串时，需要添加前缀L，比如：L”I’m-an-example-“-。这种类型的字符串，每个字符占用两个字节。当然，为了适配多语言场景，也设计了一个通用的字符串前缀，即-T（或TEXT），这个前缀也是宏定义，其在源码中的定义如下："><a href="#通常，我们使用双引号标识ANSI字符串，比如：”I’m-an-example-“-这种类型的字符串，每个字符占用一个字节。当我们想要标识UNICODE字符串时，需要添加前缀L，比如：L”I’m-an-example-“-。这种类型的字符串，每个字符占用两个字节。当然，为了适配多语言场景，也设计了一个通用的字符串前缀，即-T（或TEXT），这个前缀也是宏定义，其在源码中的定义如下：" class="headerlink" title="通常，我们使用双引号标识ANSI字符串，比如：”I’m an example. “ 这种类型的字符串，每个字符占用一个字节。当我们想要标识UNICODE字符串时，需要添加前缀L，比如：L”I’m an example. “ 。这种类型的字符串，每个字符占用两个字节。当然，为了适配多语言场景，也设计了一个通用的字符串前缀，即_T（或TEXT），这个前缀也是宏定义，其在源码中的定义如下："></a>通常，我们使用双引号标识ANSI字符串，比如：”I’m an example. “ 这种类型的字符串，每个字符占用一个字节。当我们想要标识UNICODE字符串时，需要添加前缀L，比如：L”I’m an example. “ 。这种类型的字符串，每个字符占用两个字节。当然，为了适配多语言场景，也设计了一个通用的字符串前缀，即_T（或TEXT），这个前缀也是宏定义，其在源码中的定义如下：</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _UNICODE </span></span><br><span class="line">    <span class="meta">#<span class="keyword">define</span> _T(c) L##c</span></span><br><span class="line">    <span class="meta">#<span class="keyword">define</span> TEXT(c) L##c</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span> </span></span><br><span class="line">    <span class="meta">#<span class="keyword">define</span> _T(c) c</span></span><br><span class="line">    <span class="meta">#<span class="keyword">define</span> TEXT(c) c</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><h1 id="WCHAR-LPCTSTR-LPSTR-LPWSTR"><a href="#WCHAR-LPCTSTR-LPSTR-LPWSTR" class="headerlink" title="WCHAR LPCTSTR LPSTR LPWSTR"></a>WCHAR LPCTSTR LPSTR LPWSTR</h1><h2 id="WCHAR是一种Unicode编码类型，其在源码中的定义如下："><a href="#WCHAR是一种Unicode编码类型，其在源码中的定义如下：" class="headerlink" title="WCHAR是一种Unicode编码类型，其在源码中的定义如下："></a>WCHAR是一种Unicode编码类型，其在源码中的定义如下：</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">if</span> !defined(_NATIVE_WCHAR_T_DEFINED)</span></span><br><span class="line">    <span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">short</span> WCHAR;</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">     <span class="keyword">typedef</span> <span class="type">wchar_t</span> WCHAR;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><h2 id="LPCTSTR是一种指针类型，该类型名可按照如下方式解读：LP-Pointer-C-Constant-T-TCHAR-STR-String-根据我们创建工程时的配置，LPCTSTR会映射为LPCSTR（ANSI）或LPCWSTR（Unicode）。"><a href="#LPCTSTR是一种指针类型，该类型名可按照如下方式解读：LP-Pointer-C-Constant-T-TCHAR-STR-String-根据我们创建工程时的配置，LPCTSTR会映射为LPCSTR（ANSI）或LPCWSTR（Unicode）。" class="headerlink" title="LPCTSTR是一种指针类型，该类型名可按照如下方式解读：LP - Pointer, C - Constant, T - TCHAR, STR - String.根据我们创建工程时的配置，LPCTSTR会映射为LPCSTR（ANSI）或LPCWSTR（Unicode）。"></a>LPCTSTR是一种指针类型，该类型名可按照如下方式解读：<br>LP - Pointer, C - Constant, T - TCHAR, STR - String.<br>根据我们创建工程时的配置，LPCTSTR会映射为LPCSTR（ANSI）或LPCWSTR（Unicode）。</h2><h2 id="LPSTR是一种指针类型，可能指向的数据编码类型为-ANSI-或-UTF-8，具体由protocol文件决定。LPSTR在源码中的定义如下：typedef-char-PSTR-LPSTR"><a href="#LPSTR是一种指针类型，可能指向的数据编码类型为-ANSI-或-UTF-8，具体由protocol文件决定。LPSTR在源码中的定义如下：typedef-char-PSTR-LPSTR" class="headerlink" title="LPSTR是一种指针类型，可能指向的数据编码类型为 ANSI 或 UTF-8，具体由protocol文件决定。LPSTR在源码中的定义如下：typedef char* PSTR, *LPSTR;"></a>LPSTR是一种指针类型，可能指向的数据编码类型为 ANSI 或 UTF-8，具体由protocol文件决定。LPSTR在源码中的定义如下：<br>typedef char* PSTR, *LPSTR;</h2><h2 id="LPWSTR也是一种指针类型，它用32位字符表示16位的Unicode字符，其在源码中的定义如下："><a href="#LPWSTR也是一种指针类型，它用32位字符表示16位的Unicode字符，其在源码中的定义如下：" class="headerlink" title="LPWSTR也是一种指针类型，它用32位字符表示16位的Unicode字符，其在源码中的定义如下："></a>LPWSTR也是一种指针类型，它用32位字符表示16位的Unicode字符，其在源码中的定义如下：</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="type">wchar_t</span>* LPWSTR, *PWSTR;</span><br></pre></td></tr></table></figure><h1 id="stprintf-s"><a href="#stprintf-s" class="headerlink" title="_stprintf_s"></a>_stprintf_s</h1><h2 id="stprintf-s-函数的第一个参数是要格式化的字符串，第二个参数是要插入到字符串中的值。"><a href="#stprintf-s-函数的第一个参数是要格式化的字符串，第二个参数是要插入到字符串中的值。" class="headerlink" title="_stprintf_s() 函数的第一个参数是要格式化的字符串，第二个参数是要插入到字符串中的值。"></a>_stprintf_s() 函数的第一个参数是要格式化的字符串，第二个参数是要插入到字符串中的值。</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifdef</span> UNICODE</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _stprintf_s     swprintf_s</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> _stprintf_s     sprintf_s</span></span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">TCHAR str[<span class="number">256</span>];</span><br><span class="line">_stprintf_s(str, _T(<span class="string">&quot;(%f,%f,%f)&quot;</span>), pt[<span class="number">0</span>], pt[<span class="number">1</span>], pt[<span class="number">2</span>]);</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;char和wchar-t&quot;&gt;&lt;a href=&quot;#char和wchar-t&quot; class=&quot;headerlink&quot; title=&quot;char和wchar_t&quot;&gt;&lt;/a&gt;char和wchar_t&lt;/h1&gt;&lt;h2 id=&quot;char-和-wchar-t-都是-C-中的字符类</summary>
      
    
    
    
    
    <category term="tip" scheme="https://kettycode.github.io/tags/tip/"/>
    
  </entry>
  
  <entry>
    <title>哈希表小知识点及引申</title>
    <link href="https://kettycode.github.io/2024/02/19/cpp/tip/hash/"/>
    <id>https://kettycode.github.io/2024/02/19/cpp/tip/hash/</id>
    <published>2024-02-19T04:59:59.000Z</published>
    <updated>2024-02-19T05:21:39.274Z</updated>
    
    <content type="html"><![CDATA[<h1 id="unordered-map中的小知识"><a href="#unordered-map中的小知识" class="headerlink" title="unordered_map中的小知识"></a>unordered_map中的小知识</h1><h2 id="unordered-map-将-key的某种类型-通过哈希函数映射成-size-t-即可-等价成数组"><a href="#unordered-map-将-key的某种类型-通过哈希函数映射成-size-t-即可-等价成数组" class="headerlink" title="unordered_map 将 key的某种类型 通过哈希函数映射成 size_t 即可 等价成数组"></a>unordered_map 将 key的某种类型 通过哈希函数映射成 size_t 即可 等价成数组</h2><h2 id="例如：unordered-map-map中的键（key）string-通过hash函数转化成特定的size-t-unsigned-int-类型，即和vector没太大区别"><a href="#例如：unordered-map-map中的键（key）string-通过hash函数转化成特定的size-t-unsigned-int-类型，即和vector没太大区别" class="headerlink" title="例如：unordered_map&lt;string,int&gt; map中的键（key）string 通过hash函数转化成特定的size_t(unsigned int)类型，即和vector没太大区别"></a>例如：unordered_map&lt;string,int&gt; map中的键（key）string 通过hash函数转化成特定的size_t(unsigned int)类型，即和vector<int>没太大区别</h2><h2 id="对某些没有默认哈希函数的类型可以自定义哈希函数，例如下列函数："><a href="#对某些没有默认哈希函数的类型可以自定义哈希函数，例如下列函数：" class="headerlink" title="对某些没有默认哈希函数的类型可以自定义哈希函数，例如下列函数："></a>对某些没有默认哈希函数的类型可以自定义哈希函数，例如下列函数：</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 通过 对int类型的默认哈希函数 自定义 对array&lt;int, 26&gt; 类型的哈希函数</span></span><br><span class="line"><span class="keyword">auto</span> arrayHash = [fn = hash&lt;<span class="type">int</span>&gt;&#123;&#125;] (<span class="type">const</span> array&lt;<span class="type">int</span>, <span class="number">26</span>&gt;&amp; arr) -&gt; <span class="type">size_t</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">accumulate</span>(arr.<span class="built_in">begin</span>(), arr.<span class="built_in">end</span>(), <span class="number">0u</span>, [&amp;](<span class="type">size_t</span> acc, <span class="type">int</span> num) &#123;</span><br><span class="line">            <span class="built_in">return</span> (acc &lt;&lt; <span class="number">1</span>) ^ <span class="built_in">fn</span>(num);</span><br><span class="line">      &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">unordered_map&lt;array&lt;<span class="type">int</span>, 26&gt;, vector&lt;string&gt;, <span class="keyword">decltype</span>(arrayHash)&gt; <span class="built_in">mp</span>(<span class="number">0</span>, arrayHash);</span><br></pre></td></tr></table></figure><h1 id="上述函数用到的lambda表达式"><a href="#上述函数用到的lambda表达式" class="headerlink" title="上述函数用到的lambda表达式"></a>上述函数用到的lambda表达式</h1><h2 id="lambda-lambda本质上是一个普通的函数，只是它不像普通函数这样声明，它是我们的代码在过程中生成的，用完即弃的函数，不算一个真正的函数，是匿名函数-。格式：-形参表-函数内容-返回类型-函数内容"><a href="#lambda-lambda本质上是一个普通的函数，只是它不像普通函数这样声明，它是我们的代码在过程中生成的，用完即弃的函数，不算一个真正的函数，是匿名函数-。格式：-形参表-函数内容-返回类型-函数内容" class="headerlink" title="lambda:lambda本质上是一个普通的函数，只是它不像普通函数这样声明，它是我们的代码在过程中生成的，用完即弃的函数，不算一个真正的函数，是匿名函数 。格式：[] ({形参表}) {函数内容} &#x2F;  -&gt;返回类型{函数内容}"></a>lambda:lambda本质上是一个普通的函数，只是它不像普通函数这样声明，<br>它是我们的代码在过程中生成的，用完即弃的函数，不算一个真正的函数，是匿名函数 。<br>格式：[] ({形参表}) {函数内容} &#x2F;  <a href="%7B%E5%BD%A2%E5%8F%82%E8%A1%A8%7D"></a>-&gt;返回类型{函数内容}</h2><h2 id="中括号表示的是捕获，作用是如何传递变量-lambda使用外部（相对）的变量时，就要使用捕获。如果使用捕获-则："><a href="#中括号表示的是捕获，作用是如何传递变量-lambda使用外部（相对）的变量时，就要使用捕获。如果使用捕获-则：" class="headerlink" title="中括号表示的是捕获，作用是如何传递变量 lambda使用外部（相对）的变量时，就要使用捕获。如果使用捕获,则："></a>中括号表示的是捕获，作用是如何传递变量 lambda使用外部（相对）的变量时，就要使用捕获。<br>如果使用捕获,则：</h2><h2 id="添加头文件：-include-functional-捕获-使用方式：-，则是将所有变量值传递到lambda中-，则是将所有变量引用传递到lambda中-a-是将变量a通过值传递，如果是-a-就是将变量a引用传递它可以有0个或者多个捕获"><a href="#添加头文件：-include-functional-捕获-使用方式：-，则是将所有变量值传递到lambda中-，则是将所有变量引用传递到lambda中-a-是将变量a通过值传递，如果是-a-就是将变量a引用传递它可以有0个或者多个捕获" class="headerlink" title="添加头文件： #include&lt; functional &gt;捕获[]使用方式：[&#x3D;]，则是将所有变量值传递到lambda中[&amp;]，则是将所有变量引用传递到lambda中[a]是将变量a通过值传递，如果是[&amp;a]就是将变量a引用传递它可以有0个或者多个捕获"></a>添加头文件： #include&lt; functional &gt;<br>捕获[]使用方式：<br>[&#x3D;]，则是将所有变量值传递到lambda中<br>[&amp;]，则是将所有变量引用传递到lambda中<br>[a]是将变量a通过值传递，如果是[&amp;a]就是将变量a引用传递<br>它可以有0个或者多个捕获</h2>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;unordered-map中的小知识&quot;&gt;&lt;a href=&quot;#unordered-map中的小知识&quot; class=&quot;headerlink&quot; title=&quot;unordered_map中的小知识&quot;&gt;&lt;/a&gt;unordered_map中的小知识&lt;/h1&gt;&lt;h2 id=&quot;u</summary>
      
    
    
    
    
    <category term="tip" scheme="https://kettycode.github.io/tags/tip/"/>
    
  </entry>
  
  <entry>
    <title>第一节</title>
    <link href="https://kettycode.github.io/2024/02/06/cmake/cmake1/"/>
    <id>https://kettycode.github.io/2024/02/06/cmake/cmake1/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:16:16.992Z</updated>
    
    <content type="html"><![CDATA[<h1 id="step-1-a-basic-starting-point"><a href="#step-1-a-basic-starting-point" class="headerlink" title="step 1 : a basic starting point"></a>step 1 : a basic starting point</h1><h2 id="Exercise-1-building-a-basic-project"><a href="#Exercise-1-building-a-basic-project" class="headerlink" title="Exercise 1 - building a basic project"></a>Exercise 1 - building a basic project</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># MakeLists.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#cmake版本最低要求</span></span><br><span class="line">cmake_minimum_required(VERSION 3.0)</span><br><span class="line"></span><br><span class="line"><span class="comment">#项目名称</span></span><br><span class="line">project(MyProject)</span><br><span class="line"></span><br><span class="line"><span class="comment">#添加项目所需的源代码文件</span></span><br><span class="line">add_executable(MyProject main.cpp)</span><br></pre></td></tr></table></figure><h2 id="Exercise-2-spacifying-the-C-Standard"><a href="#Exercise-2-spacifying-the-C-Standard" class="headerlink" title="Exercise 2 - spacifying the C++ Standard"></a>Exercise 2 - spacifying the C++ Standard</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># MakeLists.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#cmake版本最低要求</span></span><br><span class="line">cmake_minimum_required(VERSION 3.0)</span><br><span class="line"></span><br><span class="line"><span class="comment">#设置C++标准</span></span><br><span class="line"><span class="built_in">set</span>(CMAKE_CXX_STANDARD 11)</span><br><span class="line"></span><br><span class="line"><span class="comment">#设置C++标准为必须</span></span><br><span class="line"><span class="built_in">set</span>(CAMKE_CXX_STANDARD_REQUIRED True)</span><br><span class="line"></span><br><span class="line"><span class="comment">#项目名称</span></span><br><span class="line">project(MyProject)</span><br><span class="line"></span><br><span class="line"><span class="comment">#添加项目所需的源代码文件</span></span><br><span class="line">add_executable(MyProject main.cpp)</span><br></pre></td></tr></table></figure><h2 id="Exercise-3-adding-a-version-number-and-configured-header-file"><a href="#Exercise-3-adding-a-version-number-and-configured-header-file" class="headerlink" title="Exercise 3 - adding a version number and configured header file"></a>Exercise 3 - adding a version number and configured header file</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># MakeLists.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#cmake版本最低要求</span></span><br><span class="line">cmake_minimum_required(VERSION 3.0)</span><br><span class="line"></span><br><span class="line"><span class="comment">#项目名称</span></span><br><span class="line">project(MyProject Version 1.0)</span><br><span class="line"></span><br><span class="line">configure_file(MyProjectConfig.h.in MyProjectConfig.h)</span><br><span class="line"></span><br><span class="line">target_include_directories(MyProject PUBLIC <span class="variable">$&#123;PROJECT_BINARY_DIR&#125;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#添加项目所需的源代码文件</span></span><br><span class="line">add_executable(MyProject main.cpp)</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># MyProjectConfig.h.in</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#define VERSION_MAJOR @MyProject_VERSION_MAJOR@</span></span><br><span class="line"><span class="comment">#define VERSION_MINOR @MyProject_VERSION_MINOR@</span></span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"># main.cpp</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#inlcude <span class="string">&lt;MyProjectconfig.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (argc &lt; <span class="number">2</span>)</span><br><span class="line">&#123;</span><br><span class="line">    std::cout &lt;&lt; argv[<span class="number">0</span>] &lt;&lt; <span class="string">&quot; Version &quot;</span> &lt;&lt; VERSION_MAJOR &lt;&lt; <span class="string">&quot; . &quot;</span> </span><br><span class="line">          &lt;&lt; VERSION_MINOR &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot; Usage: &quot;</span> &lt;&lt; argv[<span class="number">0</span>] &lt;&lt; <span class="string">&quot; number &quot;</span> &lt;&lt; std:: endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;step-1-a-basic-starting-point&quot;&gt;&lt;a href=&quot;#step-1-a-basic-starting-point&quot; class=&quot;headerlink&quot; title=&quot;step 1 : a basic starting point&quot;&gt;&lt;</summary>
      
    
    
    
    
    <category term="cmake" scheme="https://kettycode.github.io/tags/cmake/"/>
    
  </entry>
  
  <entry>
    <title>第二节</title>
    <link href="https://kettycode.github.io/2024/02/06/cmake/cmake2/"/>
    <id>https://kettycode.github.io/2024/02/06/cmake/cmake2/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:16:25.373Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Step-2-adding-a-library"><a href="#Step-2-adding-a-library" class="headerlink" title="Step 2:adding a library"></a>Step 2:adding a library</h1><h2 id="Exercise-1-creating-a-library"><a href="#Exercise-1-creating-a-library" class="headerlink" title="Exercise 1 - creating a library"></a>Exercise 1 - creating a library</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)</span><br><span class="line"></span><br><span class="line">add_subdirectory(MathFunctions)</span><br><span class="line"></span><br><span class="line">target_link_libraries(Tutorial PUBLIC MathFunctions)</span><br><span class="line"></span><br><span class="line">target_include_directories(Tutorial PUBLIC </span><br><span class="line">                        <span class="string">&quot;<span class="variable">$&#123;PROJECT_BINARY_DIR&#125;</span>&quot;</span> </span><br><span class="line">                        <span class="string">&quot;<span class="variable">$&#123;PROJECT_SOURCE_DIR&#125;</span>/MathFunctions&quot;</span></span><br><span class="line">                        )</span><br></pre></td></tr></table></figure><h2 id="Exercise-2-adding-an-option"><a href="#Exercise-2-adding-an-option" class="headerlink" title="Exercise 2 - adding an option"></a>Exercise 2 - adding an option</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># MathFunctions/CMakeLists.txt</span></span><br><span class="line">option(USE_MYMATH <span class="string">&quot;Use tutorialc provided math implementation&quot;</span> ON)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(USE_MYMATH)</span><br><span class="line">    target_compile_definitions(MathFunctions PRIVATE <span class="string">&quot;USE_MYmATH&quot;</span>)</span><br><span class="line">    add_library(SqrtLibrary STATIC</span><br><span class="line">            mysqrt.cxx</span><br><span class="line">            )</span><br><span class="line">    target_link_libraries(MathFunctions PRIVATE SqrtLibrary)</span><br><span class="line">endif()</span><br><span class="line"></span><br><span class="line">add_library(MathFunction MathFunctions.cxx)</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># MathFunctions/MathFunctions.cxx</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#ifdef USE_MYMATH</span></span><br><span class="line">    <span class="built_in">return</span> detail::mysqrt(x);</span><br><span class="line"><span class="comment">#else</span></span><br><span class="line">    <span class="built_in">return</span> std::sqrt(x);</span><br><span class="line"><span class="comment">#endif</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#ifdef USE_MYMATH</span></span><br><span class="line"><span class="comment">#   include &quot;mysqrt.h&quot;</span></span><br><span class="line"><span class="comment">#endif</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#include  &lt;cmath&gt;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Step-2-adding-a-library&quot;&gt;&lt;a href=&quot;#Step-2-adding-a-library&quot; class=&quot;headerlink&quot; title=&quot;Step 2:adding a library&quot;&gt;&lt;/a&gt;Step 2:adding a l</summary>
      
    
    
    
    
    <category term="cmake" scheme="https://kettycode.github.io/tags/cmake/"/>
    
  </entry>
  
  <entry>
    <title>git学习从0开始</title>
    <link href="https://kettycode.github.io/2024/02/06/git/git/"/>
    <id>https://kettycode.github.io/2024/02/06/git/git/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-08T12:17:50.096Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-初始化配置"><a href="#1-初始化配置" class="headerlink" title="1 初始化配置"></a>1 初始化配置</h1><h2 id="1-1-配置用户名和邮箱"><a href="#1-1-配置用户名和邮箱" class="headerlink" title="1.1 配置用户名和邮箱"></a>1.1 配置用户名和邮箱</h2><p>git config –global user.name  ketty(“ketty master”)<br>git config –global user.email <a href="mailto:&#x6b;&#101;&#116;&#x74;&#121;&#x40;&#x67;&#109;&#x61;&#x69;&#x6c;&#46;&#99;&#111;&#109;">&#x6b;&#101;&#116;&#x74;&#121;&#x40;&#x67;&#109;&#x61;&#x69;&#x6c;&#46;&#99;&#111;&#109;</a><br>参数含义：<br>省略(local) : 本地配置，只对本地仓库有效<br>–global : 全局配置，对所有仓库有效<br>–system : 系统配置，对所有用户有效</p><h2 id="1-2-保存用户名和密码"><a href="#1-2-保存用户名和密码" class="headerlink" title="1.2 保存用户名和密码"></a>1.2 保存用户名和密码</h2><p>git config –global credential.helper store</p><h2 id="1-3-查看git的配置信息"><a href="#1-3-查看git的配置信息" class="headerlink" title="1.3 查看git的配置信息"></a>1.3 查看git的配置信息</h2><p>git config –glabal –list</p><h1 id="2-新建仓库"><a href="#2-新建仓库" class="headerlink" title="2 新建仓库"></a>2 新建仓库</h1><h2 id="2-1-创建一个仓库"><a href="#2-1-创建一个仓库" class="headerlink" title="2.1 创建一个仓库"></a>2.1 创建一个仓库</h2><p>git init<br>git init my-repo</p><h2 id="2-2-克隆一个仓库"><a href="#2-2-克隆一个仓库" class="headerlink" title="2.2 克隆一个仓库"></a>2.2 克隆一个仓库</h2><p>git clone </p><h1 id="3-工作区域和文件状态"><a href="#3-工作区域和文件状态" class="headerlink" title="3 工作区域和文件状态"></a>3 工作区域和文件状态</h1><h2 id="3-1-工作区域"><a href="#3-1-工作区域" class="headerlink" title="3.1 工作区域"></a>3.1 工作区域</h2><p>工作区(Working Directory) : 实际操作的目录<br>暂存区(Staging Area&#x2F;Index) : 中间区域,临时存放即将提交的修改内容<br>本地仓库(Local Repository) : git存储代码和版本信息的主要位置<br>git add : 工作区 -&gt; 暂存区<br>git commit : 暂存区 -&gt; 本地仓库</p><h2 id="3-2-git中的文件状态"><a href="#3-2-git中的文件状态" class="headerlink" title="3.2 git中的文件状态"></a>3.2 git中的文件状态</h2><p>未跟踪(Untrack)<br>未修改(Unmodified)<br>已暂存(Modified)<br>已提交(Staged)</p><h1 id="4-添加和提交文件"><a href="#4-添加和提交文件" class="headerlink" title="4 添加和提交文件"></a>4 添加和提交文件</h1><p>git status &#x2F;&#x2F;查看仓库状态<br>git add <file>&#x2F;&#x2F;添加到暂存区<br>git add .&#x2F;&#x2F;添加所有文件<br>git commit -m “描述”&#x2F;&#x2F;提交<br>git commit -am “描述” &#x2F;&#x2F;工作区-&gt;本地仓库<br>git log &#x2F;&#x2F;打印git记录 ,– oneline 查看简洁的提交记录<br>git rm –cached <file>… &#x2F;&#x2F;将提交到暂存的文件移除出来</p><h1 id="5-回退版本"><a href="#5-回退版本" class="headerlink" title="5 回退版本"></a>5 回退版本</h1><p>git reset –soft &#x2F;&#x2F;回退到某个版本，保留工作区和暂存区的所有修改内容<br>git reset –hard &#x2F;&#x2F;回退到某个版本，丢弃工作区和暂存区的所有修改内容<br>git reset –mixed &#x2F;&#x2F;回退到某个版本，只保留工作区的修改内容</p><h1 id="6-查看差异"><a href="#6-查看差异" class="headerlink" title="6 查看差异"></a>6 查看差异</h1><p>git diff &#x2F;&#x2F; 工作区 vs 暂存区<br>git diff HEAD &#x2F;&#x2F;工作区 + 暂存区 vs 本地仓库<br>git diff cached &#x2F;&#x2F;暂存区 vs 本地仓库<br>git diff staged &#x2F;&#x2F;暂存区 vs 本地仓库<br>git diff <commit_hash> <commit_hash> &#x2F;&#x2F;提交之间的差异<br>git diff HEAD~HEAD &#x2F;&#x2F;提交之间的差异<br>git diff <branch_name> <branch_name> &#x2F;&#x2F;分支之间的差异</p><h1 id="7-删除文件"><a href="#7-删除文件" class="headerlink" title="7 删除文件"></a>7 删除文件</h1><p>&#x2F;&#x2F;删除之后记得提交<br>rm file ; gti add file &#x2F;&#x2F;删除工作区的文件，然后暂存删除文件<br>git rm <file> &#x2F;&#x2F;把文件从工作区和暂存区同时删除<br>git rm –cached <file> &#x2F;&#x2F;把文件从暂存区删除<br>git rm -r* &#x2F;&#x2F;递归删除某个目录下的所有子目录和文件</p><h1 id="8-忽略文件"><a href="#8-忽略文件" class="headerlink" title="8 忽略文件"></a>8 忽略文件</h1><p>.gitignore文件</p><h1 id="9-SSH配置和克隆仓库"><a href="#9-SSH配置和克隆仓库" class="headerlink" title="9 SSH配置和克隆仓库"></a>9 SSH配置和克隆仓库</h1><p>ssh-keygen -t rsa -b 4096<br>&#x2F;&#x2F;私匙文件：id_rsa<br>&#x2F;&#x2F;公匙文件：id_rsa.pub</p><p>git push <remote> <branch> &#x2F;&#x2F;推送更新内容<br>git pull <remote> &#x2F;&#x2F;拉取更新内容</p><h1 id="10-关联本地仓库和远程仓库"><a href="#10-关联本地仓库和远程仓库" class="headerlink" title="10 关联本地仓库和远程仓库"></a>10 关联本地仓库和远程仓库</h1><p>&#x2F;&#x2F;添加远程仓库<br>1.git remote add &lt;远程仓库别名&gt; &lt;远程仓库地址&gt;<br>2.git push -u &lt;远程仓库别名&gt; &lt;远程分支名&gt;:&lt;本地分支名&gt;</p><p>git remote -v &#x2F;&#x2F;查看远程仓库<br>git pull &lt;远程仓库别名&gt; &lt;远程分支名&gt;:&lt;本地分支名&gt; &#x2F;&#x2F;拉取远程仓库内容<br>(相同可省略冒号及后面的部分)</p><h1 id="分支基本操作"><a href="#分支基本操作" class="headerlink" title="分支基本操作"></a>分支基本操作</h1><p>git branch &#x2F;&#x2F;查看分支列表<br>git branch &lt;新分支名&gt; &#x2F;&#x2F;创建新分支<br>git checkout &lt;新分支名&gt; &#x2F;&#x2F;切换新分支<br>git switch &lt;新分支名&gt; &#x2F;&#x2F;切换新分支<br>git merge &lt;新分支名&gt; &#x2F;&#x2F;合并分支<br>git branch -d &lt;新分支名&gt; &#x2F;&#x2F;删除已合并分支<br>git branch -D &lt;新分支名&gt; &#x2F;&#x2F;删除未合并分支</p><h1 id="分支合并冲突"><a href="#分支合并冲突" class="headerlink" title="分支合并冲突"></a>分支合并冲突</h1><p>手动更改文件解决合并冲突<br>git merge –about &#x2F;&#x2F;终止合并</p><h1 id="回退和rebase"><a href="#回退和rebase" class="headerlink" title="回退和rebase"></a>回退和rebase</h1><p>git rebase &lt;分支名&gt;</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-初始化配置&quot;&gt;&lt;a href=&quot;#1-初始化配置&quot; class=&quot;headerlink&quot; title=&quot;1 初始化配置&quot;&gt;&lt;/a&gt;1 初始化配置&lt;/h1&gt;&lt;h2 id=&quot;1-1-配置用户名和邮箱&quot;&gt;&lt;a href=&quot;#1-1-配置用户名和邮箱&quot; class=&quot;</summary>
      
    
    
    
    
    <category term="git" scheme="https://kettycode.github.io/tags/git/"/>
    
  </entry>
  
  <entry>
    <title>linux网络编程基础知识</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/linux%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/base1/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/linux%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/base1/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-08T03:32:24.893Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-分层模型及典型协议"><a href="#1-分层模型及典型协议" class="headerlink" title="1.分层模型及典型协议"></a>1.分层模型及典型协议</h1><p>应用层 常见的协议有HTTP协议，FTP协议。<br>传输层 常见协议有TCP&#x2F;UDP协议。<br>网络层 常见协议有IP协议、ICMP协议、IGMP协议。<br>网络接口层 常见协议有ARP协议、RARP协议。<br>TCP传输控制协议（Transmission Control Protocol）是一种面向连接的、可靠的、基于字节流的传输层通信协议。<br>UDP用户数据报协议（User Datagram Protocol）是OSI参考模型中一种无连接的传输层协议，提供面向事务的简单不可靠信息传送服务。<br>HTTP超文本传输协议（Hyper Text Transfer Protocol）是互联网上应用最为广泛的一种网络协议。<br>FTP文件传输协议（File Transfer Protocol）<br>IP协议是因特网互联协议（Internet Protocol）<br>ICMP协议是Internet控制报文协议（Internet Control Message Protocol）它是TCP&#x2F;IP协议族的一个子协议，用于在IP主机、路由器之间传递控制消息。<br>IGMP协议是 Internet 组管理协议（Internet Group Management Protocol），是因特网协议家族中的一个组播协议。该协议运行在主机和组播路由器之间。<br>ARP协议是正向地址解析协议（Address Resolution Protocol），通过已知的IP，寻找对应主机的MAC地址。<br>RARP是反向地址转换协议，通过MAC地址确定IP地址。</p><p>网络层负责点到点（ptop，point-to-point）的传输（这里的“点”指主机或路由器），而传输层负责端到端（etoe，end-to-end）的传输（这里的“端”指源主机和目的主机）。传输层可选择TCP或UDP协议。</p><h1 id="2-TCP和UDP"><a href="#2-TCP和UDP" class="headerlink" title="2.TCP和UDP"></a>2.TCP和UDP</h1><p>TCP是一种面向连接的、可靠的协议，有点像打电话，双方拿起电话互通身份之后就建立了连接，然后说话就行了，这边说的话那边保证听得到，并且是按说话的顺序听到的，说完话挂机断开连接。也就是说TCP传输的双方需要首先建立连接，之后由TCP协议保证数据收发的可靠性，丢失的数据包自动重发，上层应用程序收到的总是可靠的数据流，通讯之后关闭连接。<br>UDP是无连接的传输协议，不保证可靠性，有点像寄信，信写好放到邮筒里，既不能保证信件在邮递过程中不会丢失，也不能保证信件寄送顺序。使用UDP协议的应用程序需要自己完成丢包重发、消息排序等工作。</p><p>应用程序所看到的数据是一个整体，或说是一个流（stream），在底层通讯中这些数据可能被拆成很多数据包来发送，但是一个数据包有多少字节对应用程序是不可见的，因此TCP协议是面向流的协议。而UDP是面向消息的协议，每个UDP段都是一条消息，应用程序必须以消息为单位提取数据，不能一次提取任意字节的数据，这一点和TCP是很不同的。</p><p>3.tcp建立连接和断开连接-三次握手、四次挥手</p><p>建立连接（三次握手）的过程：<br>1.客户端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的段1。<br>客户端发出段1，SYN位表示连接请求。序号是1000，这个序号在网络通讯中用作临时的地址，每发一个数据字节，这个序号要加1，这样在接收端可以根据序号排出数据包的正确顺序，也可以发现丢包的情况，另外，规定SYN位和FIN位也要占一个序号，这次虽然没发数据，但是由于发了SYN位，因此下次再发送应该用序号1001。mss表示最大段尺寸，如果一个段太大，封装成帧后超过了链路层的最大帧长度，就必须在IP层分片，为了避免这种情况，客户端声明自己的最大段尺寸，建议服务器端发来的段不要超过这个长度。<br>2.服务器端回应客户端，是三次握手中的第2个报文段，同时带ACK标志和SYN标志。它表示对刚才客户端SYN的回应；同时又发送SYN给客户端，询问客户端是否准备好进行数据通讯。<br>服务器发出段2，也带有SYN位，同时置ACK位表示确认，确认序号是1001，表示“我接收到序号1000及其以前所有的段，请你下次发送序号为1001的段”，也就是应答了客户端的连接请求，同时也给客户端发出一个连接请求，同时声明最大尺寸为1024。<br>3.客户必须再次回应服务器端一个ACK报文，这是报文段3。<br>客户端发出段3，对服务器的连接请求进行应答，确认序号是8001。在这个过程中，客户端和服务器分别给对方发了连接请求，也应答了对方的连接请求，其中服务器的请求和应答在一个段中发出，因此一共有三个段用于建立连接，称为“三方握手（three-way-handshake）”。在建立连接的同时，双方协商了一些信息，例如双方发送序号的初始值、最大段尺寸等。</p><p>关闭连接（四次握手）的过程：<br>由于TCP连接是全双工的，因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动，一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭，而另一方执行被动关闭。<br>1.客户端发出，FIN位表示关闭连接的请求。<br>2.服务器发出，应答客户端的关闭连接请求。<br>3.服务器发出，其中也包含FIN位，向客户端发送关闭连接请求。<br>4.客户端发出，应答服务器的关闭连接请求。<br>建立连接的过程是三方握手，而关闭连接通常需要4个段，服务器的应答和关闭连接请求通常不合并在一个段中，因为有连接半关闭的情况，这种情况下客户端关闭连接之后就不能再发送数据给服务器了，但是服务器还可以发送数据给客户端，直到服务器也关闭连接为止。</p><h1 id="4-滑动窗口"><a href="#4-滑动窗口" class="headerlink" title="4.滑动窗口"></a>4.滑动窗口</h1><p>滑动窗口:随着应用程序提走数据，虚线框（接收缓冲区）是向右滑动的，因此称为滑动窗口。</p><h1 id="5-tcp-状态转换"><a href="#5-tcp-状态转换" class="headerlink" title="5.tcp 状态转换"></a>5.tcp 状态转换</h1><p>CLOSED：表示初始状态。<br>LISTEN：该状态表示服务器端的某个SOCKET处于监听状态，可以接受连接。<br>SYN_SENT：这个状态与SYN_RCVD遥相呼应，当客户端SOCKET执行CONNECT连接时，它首先发送SYN报文，随即进入到了SYN_SENT状态，并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。<br>SYN_RCVD: 该状态表示接收到SYN报文，在正常情况下，这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态，很短暂。此种状态时，当收到客户端的ACK报文后，会进入到ESTABLISHED状态。<br>ESTABLISHED：表示连接已经建立。<br>FIN_WAIT_1:  FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。区别是：<br>FIN_WAIT_1状态是当socket在ESTABLISHED状态时，想主动关闭连接，向对方发送了FIN报文，此时该socket进入到FIN_WAIT_1状态。<br>FIN_WAIT_2状态是当对方回应ACK后，该socket进入到FIN_WAIT_2状态，正常情况下，对方应马上回应ACK报文，所以FIN_WAIT_1状态一般较难见到，而FIN_WAIT_2状态可用netstat看到。<br>FIN_WAIT_2：主动关闭链接的一方，发出FIN收到ACK以后进入该状态。称之为半连接或半关闭状态。该状态下的socket只能接收数据，不能发。<br>TIME_WAIT: 表示收到了对方的FIN报文，并发送出了ACK报文，等2MSL后即可回到CLOSED可用状态。如果FIN_WAIT_1状态下，收到对方同时带 FIN标志和ACK标志的报文时，可以直接进入到TIME_WAIT状态，而无须经过FIN_WAIT_2状态。<br>CLOSING: 这种状态较特殊，属于一种较罕见的状态。正常情况下，当你发送FIN报文后，按理来说是应该先收到（或同时收到）对方的 ACK报文，再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后，并没有收到对方的ACK报文，反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢？如果双方几乎在同时close一个SOCKET的话，那么就出现了双方同时发送FIN报文的情况，也即会出现CLOSING状态，表示双方都正在关闭SOCKET连接。<br>CLOSE_WAIT: 此种状态表示在等待关闭。当对方关闭一个SOCKET后发送FIN报文给自己，系统会回应一个ACK报文给对方，此时则进入到CLOSE_WAIT状态。接下来呢，察看是否还有数据发送给对方，如果没有可以 close这个SOCKET，发送FIN报文给对方，即关闭连接。所以在CLOSE_WAIT状态下，需要关闭连接。<br>LAST_ACK: 该状态是被动关闭一方在发送FIN报文后，最后等待对方的ACK报文。当收到ACK报文后，即可以进入到CLOSED可用状态。</p><h1 id="6-路由器和交换机"><a href="#6-路由器和交换机" class="headerlink" title="6.路由器和交换机"></a>6.路由器和交换机</h1><p>路由和交换之间的主要区别就是交换发生在OSI参考模型第二层（数据链路层），而路由发生在第三层，即网络层。这一区别决定了路由和交换在移动信息的过程 中需使用不同的控制信息，所以两者实现各自功能的方式是不同的。</p><h1 id="7-半关闭"><a href="#7-半关闭" class="headerlink" title="7.半关闭"></a>7.半关闭</h1><p>当TCP链接中A发送FIN请求关闭，B端回应ACK后（A端进入FIN_WAIT_2状态），B没有立即发送FIN给A时，A方处在半链接状态，此时A可以接收B发送的数据，但是A已不能再向B发送数据。<br>从程序的角度，可以使用API来控制实现半连接状态。<br>#include &lt;sys&#x2F;socket.h&gt;<br>int shutdown(int sockfd, int how);<br>sockfd: 需要关闭的socket的描述符<br>how:允许为shutdown操作选择以下几种方式:<br>    SHUT_RD(0)：关闭sockfd上的读功能，此选项将不允许sockfd进行读操作。<br>                    该套接字不再接受数据，任何当前在套接字接受缓冲区的数据将被无声的丢弃掉。<br>    SHUT_WR(1):关闭sockfd的写功能，此选项将不允许sockfd进行写操作。进程不能在对此套接字发出写操作。<br>    SHUT_RDWR(2):关闭sockfd的读写功能。相当于调用shutdown两次：首先是以SHUT_RD,然后以SHUT_WR。<br>使用close中止一个连接，但它只是减少描述符的引用计数，并不直接关闭连接，只有当描述符的引用计数为0时才关闭连接。<br>shutdown不考虑描述符的引用计数，直接关闭描述符。也可选择中止一个方向的连接，只中止读或只中止写。<br>注意:<br>1.如果有多个进程共享一个套接字，close每被调用一次，计数减1，直到计数为0时，也就是所用进程都调用了close，套接字将被释放。<br>2.在多进程中如果一个进程调用了shutdown(sfd, SHUT_RDWR)后，其它的进程将无法进行通信。但，如果一个进程close(sfd)将不会影响到其它进程。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;1-分层模型及典型协议&quot;&gt;&lt;a href=&quot;#1-分层模型及典型协议&quot; class=&quot;headerlink&quot; title=&quot;1.分层模型及典型协议&quot;&gt;&lt;/a&gt;1.分层模型及典型协议&lt;/h1&gt;&lt;p&gt;应用层 常见的协议有HTTP协议，FTP协议。&lt;br&gt;传输层 常见协</summary>
      
    
    
    
    
    <category term="linux网络编程" scheme="https://kettycode.github.io/tags/linux%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Socket编程基础</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/linux%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/socket%E7%BC%96%E7%A8%8B1/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/linux%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/socket%E7%BC%96%E7%A8%8B1/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:22:10.911Z</updated>
    
    <content type="html"><![CDATA[<p>#socket<br>Socket本身有“插座”的意思，在Linux环境下，用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。</p><p>在TCP&#x2F;IP协议中，“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识，那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。</p><p>在网络通信中，套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符索发送缓冲区和接收缓冲区。</p><p>#socket API<br>##为TCP&#x2F;IP协议设计的应用层编程接口</p><p>TCP&#x2F;IP协议规定，网络数据流应采用大端字节序，即低地址高字节<br>为使网络程序具有可移植性，使同样的C代码在大端和小端计算机上编译后都能正常运行，可以调用以下库函数做网络字节序和主机字节序的转换。<br>h表示host，n表示network，l表示32位长整数，s表示16位短整数。<br>如果主机是小端字节序，这些函数将参数做相应的大小端转换然后返回，如果主机是大端字节序，这些函数不做转换，将参数原封不动地返回。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;arpa/inet.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">htonl</span><span class="params">(<span class="type">uint32_t</span> hostlong)</span>;</span><br><span class="line"><span class="type">uint16_t</span> <span class="title function_">htons</span><span class="params">(<span class="type">uint16_t</span> hostshort)</span>;</span><br><span class="line"><span class="type">uint32_t</span> <span class="title function_">ntohl</span><span class="params">(<span class="type">uint32_t</span> netlong)</span>;</span><br><span class="line"><span class="type">uint16_t</span> <span class="title function_">ntohs</span><span class="params">(<span class="type">uint16_t</span> netshort)</span>;</span><br></pre></td></tr></table></figure><p>#IP地址转换函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//早期，只能处理IPv4的ip地址，不可重入函数</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;arpa/inet.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">inet_aton</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *cp, <span class="keyword">struct</span> in_addr *inp)</span>;</span><br><span class="line"><span class="type">in_addr_t</span> <span class="title function_">inet_addr</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *cp)</span>;</span><br><span class="line"><span class="type">char</span> *<span class="title function_">inet_ntoa</span><span class="params">(<span class="keyword">struct</span> in_addr in)</span>;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//支持IPv4和IPv6，可重入函数，其中inet_pton和inet_ntop不仅可以转换IPv4的</span></span><br><span class="line"><span class="comment">//in_addr，还可以转换IPv6的in6_addr。因此函数接口是void *addrptr。</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;arpa/inet.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">inet_pton</span><span class="params">(<span class="type">int</span> af, <span class="type">const</span> <span class="type">char</span> *src, <span class="type">void</span> *dst)</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">char</span> *<span class="title function_">inet_ntop</span><span class="params">(<span class="type">int</span> af, <span class="type">const</span> <span class="type">void</span> *src, <span class="type">char</span> *dst, <span class="type">socklen_t</span> size)</span>;</span><br></pre></td></tr></table></figure><p>#sockaddr数据结构</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr</span> &#123;</span></span><br><span class="line"><span class="type">sa_family_t</span> sa_family; <span class="comment">/* address family, AF_xxx */</span></span><br><span class="line"><span class="type">char</span> sa_data[<span class="number">14</span>];<span class="comment">/* 14 bytes of protocol address */</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> &#123;</span></span><br><span class="line"><span class="type">__kernel_sa_family_t</span> sin_family; <span class="comment">/* Address family */</span>  地址结构类型</span><br><span class="line">__be16 sin_port; <span class="comment">/* Port number */</span>端口号</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">in_addr</span> <span class="title">sin_addr</span>;</span><span class="comment">/* Internet address */</span>IP地址</span><br><span class="line"><span class="comment">/* Pad to size of `struct sockaddr&#x27;. */</span></span><br><span class="line"><span class="type">unsigned</span> <span class="type">char</span> __pad[__SOCK_SIZE__ - <span class="keyword">sizeof</span>(<span class="type">short</span> <span class="type">int</span>) -</span><br><span class="line"><span class="keyword">sizeof</span>(<span class="type">unsigned</span> <span class="type">short</span> <span class="type">int</span>) - <span class="keyword">sizeof</span>(<span class="keyword">struct</span> in_addr)];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">in_addr</span> &#123;</span><span class="comment">/* Internet address. */</span></span><br><span class="line">__be32 s_addr;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in6</span> &#123;</span></span><br><span class="line"><span class="type">unsigned</span> <span class="type">short</span> <span class="type">int</span> sin6_family; <span class="comment">/* AF_INET6 */</span></span><br><span class="line">__be16 sin6_port; <span class="comment">/* Transport layer port # */</span></span><br><span class="line">__be32 sin6_flowinfo; <span class="comment">/* IPv6 flow information */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> <span class="title">sin6_addr</span>;</span><span class="comment">/* IPv6 address */</span></span><br><span class="line">__u32 sin6_scope_id; <span class="comment">/* scope id (new in RFC2553) */</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> &#123;</span></span><br><span class="line"><span class="class"><span class="keyword">union</span> &#123;</span></span><br><span class="line">__u8 u6_addr8[<span class="number">16</span>];</span><br><span class="line">__be16 u6_addr16[<span class="number">8</span>];</span><br><span class="line">__be32 u6_addr32[<span class="number">4</span>];</span><br><span class="line">&#125; in6_u;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> s6_addr in6_u.u6_addr8</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> s6_addr16     in6_u.u6_addr16</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> s6_addr32 in6_u.u6_addr32</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> UNIX_PATH_MAX 108</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_un</span> &#123;</span></span><br><span class="line"><span class="type">__kernel_sa_family_t</span> sun_family; <span class="comment">/* AF_UNIX */</span></span><br><span class="line"><span class="type">char</span> sun_path[UNIX_PATH_MAX]; <span class="comment">/* pathname */</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>Pv4和IPv6的地址格式定义在netinet&#x2F;in.h中，IPv4地址用sockaddr_in结构体表示，包括16位端口号和32位IP地址，IPv6地址用sockaddr_in6结构体表示，包括16位端口号、128位IP地址和一些控制字段</p><p>因此，socket API可以接受各种类型的sockaddr结构体指针做参数，例如bind、accept、connect等函数，这些函数的参数应该设计成void *类型以便接受各种类型的指针，但是sock API的实现早于ANSI C标准化，那时还没有void *类型，因此这些函数的参数都用struct sockaddr *类型表示，在传递参数之前要强制类型转换一下，例如：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>;</span></span><br><span class="line">bind(listen_fd, (<span class="keyword">struct</span> sockaddr *)&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));<span class="comment">/* initialize servaddr */</span></span><br></pre></td></tr></table></figure><p>TCP客户端：socket() -&gt; connect() -&gt; write() &lt;-&gt; read() -&gt; close()<br>TCP服务端：socket() -&gt; bind() -&gt; listen() -&gt; accept() -&gt; 阻塞直到有客户端连接 -&gt; read() &lt;-&gt; write() -&gt; read() -&gt; close()</p><p>##socket函数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#include &lt;sys/types.h&gt; /* See NOTES */</span></span><br><span class="line"><span class="comment">#include &lt;sys/socket.h&gt;</span></span><br><span class="line"></span><br><span class="line">int socket(int domain, int <span class="built_in">type</span>, int protocol);</span><br><span class="line">domain:</span><br><span class="line">AF_INET 这是大多数用来产生socket的协议，使用TCP或UDP来传输，用IPv4的地址</span><br><span class="line">AF_INET6 与上面类似，不过是来用IPv6的地址</span><br><span class="line">AF_UNIX 本地协议，使用在Unix和Linux系统上，一般都是当客户端和服务器在同一台及其上的时候使用</span><br><span class="line"><span class="built_in">type</span>:</span><br><span class="line">SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型，这个socket是使用TCP来进行传输。</span><br><span class="line">SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的，使用UDP来进行它的连接。</span><br><span class="line">SOCK_SEQPACKET该协议是双线路的、可靠的连接，发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。</span><br><span class="line">SOCK_RAW socket类型提供单一的网络访问，这个socket类型使用ICMP公共协议。（ping、traceroute使用该协议）</span><br><span class="line">SOCK_RDM 这个类型是很少使用的，在大部分的操作系统上没有实现，它是提供给数据链路层使用，不保证数据包的顺序</span><br><span class="line">protocol:</span><br><span class="line">传0 表示使用默认协议。</span><br><span class="line">返回值：</span><br><span class="line">成功：返回指向新创建的socket的文件描述符，失败：返回-1，设置errno</span><br></pre></td></tr></table></figure><p>socket()打开一个网络通讯端口，如果成功的话，就像open()一样返回一个文件描述符，应用程序可以像读写文件一样用read&#x2F;write在网络上收发数据，如果socket()调用出错则返回-1。对于IPv4，domain参数指定为AF_INET。对于TCP协议，type参数指定为SOCK_STREAM，表示面向流的传输协议。如果是UDP协议，则type参数指定为SOCK_DGRAM，表示面向数据报的传输协议。protocol参数的介绍从略，指定为0即可。</p><p>##bind函数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#include &lt;sys/types.h&gt; /* See NOTES */</span></span><br><span class="line"><span class="comment">#include &lt;sys/socket.h&gt;</span></span><br><span class="line"></span><br><span class="line">int <span class="built_in">bind</span>(int sockfd, const struct sockaddr *addr, socklen_t addrlen);</span><br><span class="line">sockfd：</span><br><span class="line">socket文件描述符</span><br><span class="line">addr:</span><br><span class="line">构造出IP地址加端口号</span><br><span class="line">addrlen:</span><br><span class="line">sizeof(addr)长度</span><br><span class="line">返回值：</span><br><span class="line">成功返回0，失败返回-1, 设置errno</span><br></pre></td></tr></table></figure><p>服务器程序所监听的网络地址和端口号通常是固定不变的，客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接，因此服务器需要调用bind绑定一个固定的网络地址和端口号。</p><p>bind()的作用是将参数sockfd和addr绑定在一起，使sockfd这个用于网络通讯的文件描述符监听addr所描述的地址和端口号。前面讲过，struct sockaddr *是一个通用指针类型，addr参数实际上可以接受多种协议的sockaddr结构体，而它们的长度各不相同，所以需要第三个参数addrlen指定结构体的长度。如：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">sockaddr_in</span> servaddr;</span><br><span class="line"><span class="built_in">bzero</span>(&amp;servaddr, <span class="built_in">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">servaddr.sin_addr.s_addr = <span class="built_in">htonl</span>(INADDR_ANY);</span><br><span class="line">servaddr.sin_port = <span class="built_in">htons</span>(<span class="number">6666</span>);</span><br></pre></td></tr></table></figure><p>##listen函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span> <span class="comment">/* See NOTES */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">listen</span><span class="params">(<span class="type">int</span> sockfd, <span class="type">int</span> backlog)</span>;</span><br><span class="line">sockfd:</span><br><span class="line">socket文件描述符</span><br><span class="line">backlog:</span><br><span class="line">排队建立<span class="number">3</span>次握手队列和刚刚建立<span class="number">3</span>次握手队列的链接数和</span><br></pre></td></tr></table></figure><p>典型的服务器程序可以同时服务于多个客户端，当有客户端发起连接时，服务器调用的accept()返回并接受这个连接，如果有大量的客户端发起连接而服务器来不及处理，尚未accept的客户端就处于连接等待状态，listen()声明sockfd处于监听状态，并且最多允许有backlog个客户端处于连接待状态，如果接收到更多的连接请求就忽略。listen()成功返回0，失败返回-1。</p><p>##accept函数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#include &lt;sys/types.h&gt; /* See NOTES */</span></span><br><span class="line"><span class="comment">#include &lt;sys/socket.h&gt;</span></span><br><span class="line"></span><br><span class="line">int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);</span><br><span class="line">sockdf:</span><br><span class="line">socket文件描述符</span><br><span class="line">addr:</span><br><span class="line">传出参数，返回链接客户端地址信息，含IP地址和端口号</span><br><span class="line">addrlen:</span><br><span class="line">传入传出参数（值-结果）,传入sizeof(addr)大小，函数返回时返回真正接收到地址结构体的大小</span><br><span class="line">返回值：</span><br><span class="line">成功返回一个新的socket文件描述符，用于和客户端通信，失败返回-1，设置errno</span><br></pre></td></tr></table></figure><p>三次握手完成后，服务器调用accept()接受连接，如果服务器调用accept()时还没有客户端的连接请求，就阻塞等待直到有客户端连接上来。addr是一个传出参数，accept()返回时传出客户端的地址和端口号。addrlen参数是一个传入传出参数（value-result argument），传入的是调用者提供的缓冲区addr的长度以避免缓冲区溢出问题，传出的是客户端地址结构体的实际长度（有可能没有占满调用者提供的缓冲区）。如果给addr参数传NULL，表示不关心客户端的地址。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">cliaddr_len = <span class="keyword">sizeof</span>(cliaddr);</span><br><span class="line">connfd = accept(listenfd, (<span class="keyword">struct</span> sockaddr *)&amp;cliaddr, &amp;cliaddr_len);</span><br><span class="line">n = read(connfd, buf, MAXLINE);</span><br><span class="line">......</span><br><span class="line">close(connfd);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们的服务器程序结构整个是一个while死循环，每次循环处理一个客户端连接。由于cliaddr_len是传入传出参数，每次调用accept()之前应该重新赋初值。accept()的参数listenfd是先前的监听文件描述符，而accept()的返回值是另外一个文件描述符connfd，之后与客户端之间就通过这个connfd通讯，最后关闭connfd断开连接，而不关闭listenfd，再次回到循环开头listenfd仍然用作accept的参数。accept()成功返回一个文件描述符，出错返回-1</p><p>##connect函数</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span> <span class="comment">/* See NOTES */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">connect</span><span class="params">(<span class="type">int</span> sockfd, <span class="type">const</span> <span class="keyword">struct</span> sockaddr *addr, <span class="type">socklen_t</span> addrlen)</span>;</span><br><span class="line">sockdf:</span><br><span class="line">socket文件描述符</span><br><span class="line">addr:</span><br><span class="line">传入参数，指定服务器端地址信息，含IP地址和端口号</span><br><span class="line">addrlen:</span><br><span class="line">传入参数,传入<span class="keyword">sizeof</span>(addr)大小</span><br><span class="line">返回值：</span><br><span class="line">成功返回<span class="number">0</span>，失败返回<span class="number">-1</span>，设置errno</span><br></pre></td></tr></table></figure><p>客户端需要调用connect()连接服务器，connect和bind的参数形式一致，区别在于bind的参数是自己的地址，而connect的参数是对方的地址。connect()成功返回0，出错返回-1。</p><p>服务器调用socket()、bind()、listen()完成初始化后，调用accept()阻塞等待，处于监听端口的状态，客户端调用socket()初始化后，调用connect()发出SYN段并阻塞等待服务器应答，服务器应答一个SYN-ACK段，客户端收到后从connect()返回，同时应答一个ACK段，服务器收到后从accept()返回。<br>数据传输的过程：<br>建立连接后，TCP协议提供全双工的通信服务，但是一般的客户端&#x2F;服务器程序的流程是由客户端主动发起请求，服务器被动处理请求，一问一答的方式。因此，服务器从accept()返回后立刻调用read()，读socket就像读管道一样，如果没有数据到达就阻塞等待，这时客户端调用write()发送请求给服务器，服务器收到后从read()返回，对客户端的请求进行处理，在此期间客户端调用read()阻塞等待服务器的应答，服务器调用write()将处理结果发回给客户端，再次调用read()阻塞等待下一条请求，客户端收到后从read()返回，发送下一条请求，如此循环下去。<br>如果客户端没有更多的请求了，就调用close()关闭连接，就像写端关闭的管道一样，服务器的read()返回0，这样服务器就知道客户端关闭了连接，也调用close()关闭连接。注意，任何一方调用close()后，连接的两个传输方向都关闭，不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态，仍可接收对方发来的数据。<br>在学习socket API时要注意应用程序和TCP协议层是如何交互的： 应用程序调用某个socket函数时TCP协议层完成什么动作，比如调用connect()会发出SYN段 应用程序如何知道TCP协议层的状态变化，比如从某个阻塞的socket函数返回就表明TCP协议收到了某些段，再比如read()返回0就表明收到了FIN段</p><p>##最简单的客户端&#x2F;服务器程序</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Server</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;arpa/inet.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAXLINE 80</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SERV_PORT 6666</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>, <span class="title">cliaddr</span>;</span></span><br><span class="line"><span class="type">socklen_t</span> cliaddr_len;</span><br><span class="line"><span class="type">int</span> listenfd, connfd;</span><br><span class="line"><span class="type">char</span> buf[MAXLINE];</span><br><span class="line"><span class="type">char</span> str[INET_ADDRSTRLEN];</span><br><span class="line"><span class="type">int</span> i, n;</span><br><span class="line"></span><br><span class="line">listenfd = socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">bzero(&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">servaddr.sin_addr.s_addr = htonl(INADDR_ANY);</span><br><span class="line">servaddr.sin_port = htons(SERV_PORT);</span><br><span class="line"></span><br><span class="line">bind(listenfd, (<span class="keyword">struct</span> sockaddr *)&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">listen(listenfd, <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Accepting connections ...\n&quot;</span>);</span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">cliaddr_len = <span class="keyword">sizeof</span>(cliaddr);</span><br><span class="line">connfd = accept(listenfd, (<span class="keyword">struct</span> sockaddr *)&amp;cliaddr, &amp;cliaddr_len);</span><br><span class="line">n = read(connfd, buf, MAXLINE);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;received from %s at PORT %d\n&quot;</span>,</span><br><span class="line">inet_ntop(AF_INET, &amp;cliaddr.sin_addr, str, <span class="keyword">sizeof</span>(str)),</span><br><span class="line">ntohs(cliaddr.sin_port));</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">buf[i] = <span class="built_in">toupper</span>(buf[i]);</span><br><span class="line">write(connfd, buf, n);</span><br><span class="line">close(connfd);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Client</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/socket.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAXLINE 80</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SERV_PORT 6666</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>;</span></span><br><span class="line"><span class="type">char</span> buf[MAXLINE];</span><br><span class="line"><span class="type">int</span> sockfd, n;</span><br><span class="line"><span class="type">char</span> *str;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (argc != <span class="number">2</span>) &#123;</span><br><span class="line"><span class="built_in">fputs</span>(<span class="string">&quot;usage: ./client message\n&quot;</span>, <span class="built_in">stderr</span>);</span><br><span class="line"><span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line">str = argv[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">sockfd = socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">bzero(&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">inet_pton(AF_INET, <span class="string">&quot;127.0.0.1&quot;</span>, &amp;servaddr.sin_addr);</span><br><span class="line">servaddr.sin_port = htons(SERV_PORT);</span><br><span class="line"></span><br><span class="line">connect(sockfd, (<span class="keyword">struct</span> sockaddr *)&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line"></span><br><span class="line">write(sockfd, str, <span class="built_in">strlen</span>(str));</span><br><span class="line"></span><br><span class="line">n = read(sockfd, buf, MAXLINE);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Response from server:\n&quot;</span>);</span><br><span class="line">write(STDOUT_FILENO, buf, n);</span><br><span class="line">close(sockfd);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>##出错封装函数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br></pre></td><td class="code"><pre><span class="line">//wrap.c</span><br><span class="line"><span class="comment">#include &lt;stdlib.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;errno.h&gt;</span></span><br><span class="line"><span class="comment">#include &lt;sys/socket.h&gt;</span></span><br><span class="line">void perr_exit(const char *s)</span><br><span class="line">&#123;</span><br><span class="line">perror(s);</span><br><span class="line"><span class="built_in">exit</span>(1);</span><br><span class="line">&#125;</span><br><span class="line">int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)</span><br><span class="line">&#123;</span><br><span class="line">int n;</span><br><span class="line">again:</span><br><span class="line"><span class="keyword">if</span> ( (n = accept(fd, sa, salenptr)) &lt; 0) &#123;</span><br><span class="line"><span class="keyword">if</span> ((errno == ECONNABORTED) || (errno == EINTR))</span><br><span class="line">goto again;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">perr_exit(<span class="string">&quot;accept error&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line">int Bind(int fd, const struct sockaddr *sa, socklen_t salen)</span><br><span class="line">&#123;</span><br><span class="line">int n;</span><br><span class="line"><span class="keyword">if</span> ((n = bind(fd, sa, salen)) &lt; 0)</span><br><span class="line">perr_exit(<span class="string">&quot;bind error&quot;</span>);</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line">int Connect(int fd, const struct sockaddr *sa, socklen_t salen)</span><br><span class="line">&#123;</span><br><span class="line">int n;</span><br><span class="line"><span class="keyword">if</span> ((n = connect(fd, sa, salen)) &lt; 0)</span><br><span class="line">perr_exit(<span class="string">&quot;connect error&quot;</span>);</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line">int Listen(int fd, int backlog)</span><br><span class="line">&#123;</span><br><span class="line">int n;</span><br><span class="line"><span class="keyword">if</span> ((n = listen(fd, backlog)) &lt; 0)</span><br><span class="line">perr_exit(<span class="string">&quot;listen error&quot;</span>);</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line">int Socket(int family, int <span class="built_in">type</span>, int protocol)</span><br><span class="line">&#123;</span><br><span class="line">int n;</span><br><span class="line"><span class="keyword">if</span> ( (n = socket(family, <span class="built_in">type</span>, protocol)) &lt; 0)</span><br><span class="line">perr_exit(<span class="string">&quot;socket error&quot;</span>);</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line">ssize_t Read(int fd, void *ptr, size_t nbytes)</span><br><span class="line">&#123;</span><br><span class="line">ssize_t n;</span><br><span class="line">again:</span><br><span class="line"><span class="keyword">if</span> ( (n = <span class="built_in">read</span>(fd, ptr, nbytes)) == -1) &#123;</span><br><span class="line"><span class="keyword">if</span> (errno == EINTR)</span><br><span class="line">goto again;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="built_in">return</span> -1;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line">ssize_t Write(int fd, const void *ptr, size_t nbytes)</span><br><span class="line">&#123;</span><br><span class="line">ssize_t n;</span><br><span class="line">again:</span><br><span class="line"><span class="keyword">if</span> ( (n = write(fd, ptr, nbytes)) == -1) &#123;</span><br><span class="line"><span class="keyword">if</span> (errno == EINTR)</span><br><span class="line">goto again;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="built_in">return</span> -1;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line">int Close(int fd)</span><br><span class="line">&#123;</span><br><span class="line">int n;</span><br><span class="line"><span class="keyword">if</span> ((n = close(fd)) == -1)</span><br><span class="line">perr_exit(<span class="string">&quot;close error&quot;</span>);</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line">ssize_t Readn(int fd, void *vptr, size_t n)</span><br><span class="line">&#123;</span><br><span class="line">size_t nleft;</span><br><span class="line">ssize_t nread;</span><br><span class="line">char *ptr;</span><br><span class="line"></span><br><span class="line">ptr = vptr;</span><br><span class="line">nleft = n;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (nleft &gt; 0) &#123;</span><br><span class="line"><span class="keyword">if</span> ( (nread = <span class="built_in">read</span>(fd, ptr, nleft)) &lt; 0) &#123;</span><br><span class="line"><span class="keyword">if</span> (errno == EINTR)</span><br><span class="line">nread = 0;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="built_in">return</span> -1;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (nread == 0)</span><br><span class="line"><span class="built_in">break</span>;</span><br><span class="line">nleft -= nread;</span><br><span class="line">ptr += nread;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">return</span> n - nleft;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ssize_t Writen(int fd, const void *vptr, size_t n)</span><br><span class="line">&#123;</span><br><span class="line">size_t nleft;</span><br><span class="line">ssize_t nwritten;</span><br><span class="line">const char *ptr;</span><br><span class="line"></span><br><span class="line">ptr = vptr;</span><br><span class="line">nleft = n;</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (nleft &gt; 0) &#123;</span><br><span class="line"><span class="keyword">if</span> ( (nwritten = write(fd, ptr, nleft)) &lt;= 0) &#123;</span><br><span class="line"><span class="keyword">if</span> (nwritten &lt; 0 &amp;&amp; errno == EINTR)</span><br><span class="line">nwritten = 0;</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="built_in">return</span> -1;</span><br><span class="line">&#125;</span><br><span class="line">nleft -= nwritten;</span><br><span class="line">ptr += nwritten;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static ssize_t my_read(int fd, char *ptr)</span><br><span class="line">&#123;</span><br><span class="line">static int read_cnt;</span><br><span class="line">static char *read_ptr;</span><br><span class="line">static char read_buf[100];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (read_cnt &lt;= 0) &#123;</span><br><span class="line">again:</span><br><span class="line"><span class="keyword">if</span> ((read_cnt = read(fd, read_buf, sizeof(read_buf))) &lt; 0) &#123;</span><br><span class="line"><span class="keyword">if</span> (errno == EINTR)</span><br><span class="line">goto again;</span><br><span class="line"><span class="built_in">return</span> -1;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (read_cnt == 0)</span><br><span class="line"><span class="built_in">return</span> 0;</span><br><span class="line">read_ptr = read_buf;</span><br><span class="line">&#125;</span><br><span class="line">read_cnt--;</span><br><span class="line">*ptr = *read_ptr++;</span><br><span class="line"><span class="built_in">return</span> 1;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ssize_t Readline(int fd, void *vptr, size_t maxlen)</span><br><span class="line">&#123;</span><br><span class="line">ssize_t n, rc;</span><br><span class="line">char c, *ptr;</span><br><span class="line">ptr = vptr;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (n = 1; n &lt; maxlen; n++) &#123;</span><br><span class="line"><span class="keyword">if</span> ( (rc = my_read(fd, &amp;c)) == 1) &#123;</span><br><span class="line">*ptr++ = c;</span><br><span class="line"><span class="keyword">if</span> (c == <span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line"><span class="built_in">break</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (rc == 0) &#123;</span><br><span class="line">*ptr = 0;</span><br><span class="line"><span class="built_in">return</span> n - 1;</span><br><span class="line">&#125; <span class="keyword">else</span></span><br><span class="line"><span class="built_in">return</span> -1;</span><br><span class="line">&#125;</span><br><span class="line">*ptr = 0;</span><br><span class="line"><span class="built_in">return</span> n;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __WRAP_H_</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __WRAP_H_</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">perr_exit</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *s)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">Accept</span><span class="params">(<span class="type">int</span> fd, <span class="keyword">struct</span> sockaddr *sa, <span class="type">socklen_t</span> *salenptr)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">Bind</span><span class="params">(<span class="type">int</span> fd, <span class="type">const</span> <span class="keyword">struct</span> sockaddr *sa, <span class="type">socklen_t</span> salen)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">Connect</span><span class="params">(<span class="type">int</span> fd, <span class="type">const</span> <span class="keyword">struct</span> sockaddr *sa, <span class="type">socklen_t</span> salen)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">Listen</span><span class="params">(<span class="type">int</span> fd, <span class="type">int</span> backlog)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">Socket</span><span class="params">(<span class="type">int</span> family, <span class="type">int</span> type, <span class="type">int</span> protocol)</span>;</span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">Read</span><span class="params">(<span class="type">int</span> fd, <span class="type">void</span> *ptr, <span class="type">size_t</span> nbytes)</span>;</span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">Write</span><span class="params">(<span class="type">int</span> fd, <span class="type">const</span> <span class="type">void</span> *ptr, <span class="type">size_t</span> nbytes)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">Close</span><span class="params">(<span class="type">int</span> fd)</span>;</span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">Readn</span><span class="params">(<span class="type">int</span> fd, <span class="type">void</span> *vptr, <span class="type">size_t</span> n)</span>;</span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">Writen</span><span class="params">(<span class="type">int</span> fd, <span class="type">const</span> <span class="type">void</span> *vptr, <span class="type">size_t</span> n)</span>;</span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">my_read</span><span class="params">(<span class="type">int</span> fd, <span class="type">char</span> *ptr)</span>;</span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">Readline</span><span class="params">(<span class="type">int</span> fd, <span class="type">void</span> *vptr, <span class="type">size_t</span> maxlen)</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>##多进程并发服务器</p><p>使用多进程并发服务器时要考虑以下几点：<br>1.父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符)<br>2.系统内创建进程个数(与内存大小相关)<br>3.进程创建过多是否降低整体服务性能(进程调度)</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Server.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;arpa/inet.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/wait.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/types.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;wrap.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAXLINE 80</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SERV_PORT 800</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">do_sigchild</span><span class="params">(<span class="type">int</span> num)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">while</span> (waitpid(<span class="number">0</span>, <span class="literal">NULL</span>, WNOHANG) &gt; <span class="number">0</span>)</span><br><span class="line">;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>, <span class="title">cliaddr</span>;</span></span><br><span class="line"><span class="type">socklen_t</span> cliaddr_len;</span><br><span class="line"><span class="type">int</span> listenfd, connfd;</span><br><span class="line"><span class="type">char</span> buf[MAXLINE];</span><br><span class="line"><span class="type">char</span> str[INET_ADDRSTRLEN];</span><br><span class="line"><span class="type">int</span> i, n;</span><br><span class="line"><span class="type">pid_t</span> pid;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span> <span class="title">newact</span>;</span></span><br><span class="line">newact.sa_handler = do_sigchild;</span><br><span class="line">sigemptyset(&amp;newact.sa_mask);</span><br><span class="line">newact.sa_flags = <span class="number">0</span>;</span><br><span class="line">sigaction(SIGCHLD, &amp;newact, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">listenfd = Socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">bzero(&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">servaddr.sin_addr.s_addr = htonl(INADDR_ANY);</span><br><span class="line">servaddr.sin_port = htons(SERV_PORT);</span><br><span class="line"></span><br><span class="line">Bind(listenfd, (<span class="keyword">struct</span> sockaddr *)&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line"></span><br><span class="line">Listen(listenfd, <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Accepting connections ...\n&quot;</span>);</span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">cliaddr_len = <span class="keyword">sizeof</span>(cliaddr);</span><br><span class="line">connfd = Accept(listenfd, (<span class="keyword">struct</span> sockaddr *)&amp;cliaddr, &amp;cliaddr_len);</span><br><span class="line"></span><br><span class="line">pid = fork();</span><br><span class="line"><span class="keyword">if</span> (pid == <span class="number">0</span>) &#123;</span><br><span class="line">Close(listenfd);</span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">n = Read(connfd, buf, MAXLINE);</span><br><span class="line"><span class="keyword">if</span> (n == <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;the other side has been closed.\n&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;received from %s at PORT %d\n&quot;</span>,</span><br><span class="line">inet_ntop(AF_INET, &amp;cliaddr.sin_addr, str, <span class="keyword">sizeof</span>(str)),</span><br><span class="line">ntohs(cliaddr.sin_port));</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">buf[i] = <span class="built_in">toupper</span>(buf[i]);</span><br><span class="line">Write(connfd, buf, n);</span><br><span class="line">&#125;</span><br><span class="line">Close(connfd);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (pid &gt; <span class="number">0</span>) &#123;</span><br><span class="line">Close(connfd);</span><br><span class="line">&#125; <span class="keyword">else</span></span><br><span class="line">perr_exit(<span class="string">&quot;fork&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line">Close(listenfd);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Client.c</span></span><br><span class="line"><span class="comment">/* client.c */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;wrap.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAXLINE 80</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SERV_PORT 6666</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>;</span></span><br><span class="line"><span class="type">char</span> buf[MAXLINE];</span><br><span class="line"><span class="type">int</span> sockfd, n;</span><br><span class="line"></span><br><span class="line">sockfd = Socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">bzero(&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">inet_pton(AF_INET, <span class="string">&quot;127.0.0.1&quot;</span>, &amp;servaddr.sin_addr);</span><br><span class="line">servaddr.sin_port = htons(SERV_PORT);</span><br><span class="line"></span><br><span class="line">Connect(sockfd, (<span class="keyword">struct</span> sockaddr *)&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (fgets(buf, MAXLINE, <span class="built_in">stdin</span>) != <span class="literal">NULL</span>) &#123;</span><br><span class="line">Write(sockfd, buf, <span class="built_in">strlen</span>(buf));</span><br><span class="line">n = Read(sockfd, buf, MAXLINE);</span><br><span class="line"><span class="keyword">if</span> (n == <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;the other side has been closed.\n&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125; <span class="keyword">else</span></span><br><span class="line">Write(STDOUT_FILENO, buf, n);</span><br><span class="line">&#125;</span><br><span class="line">Close(sockfd);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在使用线程模型开发服务器时需考虑以下问题：<br>1.调整进程内最大文件描述符上限<br>2.线程如有共享数据，考虑线程同步<br>3.服务于客户端线程退出时，退出处理。（退出值，分离态）<br>4.系统负载，随着链接客户端增加，导致其它线程不能及时得到CPU</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* server.c */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;arpa/inet.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;wrap.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAXLINE 80</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SERV_PORT 6666</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">s_info</span> &#123;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">cliaddr</span>;</span></span><br><span class="line"><span class="type">int</span> connfd;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="type">void</span> *<span class="title function_">do_work</span><span class="params">(<span class="type">void</span> *arg)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="type">int</span> n,i;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">s_info</span> *<span class="title">ts</span> =</span> (<span class="keyword">struct</span> s_info*)arg;</span><br><span class="line"><span class="type">char</span> buf[MAXLINE];</span><br><span class="line"><span class="type">char</span> str[INET_ADDRSTRLEN];</span><br><span class="line"><span class="comment">/* 可以在创建线程前设置线程创建属性,设为分离态,哪种效率高内？ */</span></span><br><span class="line">pthread_detach(pthread_self());</span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">n = Read(ts-&gt;connfd, buf, MAXLINE);</span><br><span class="line"><span class="keyword">if</span> (n == <span class="number">0</span>) &#123;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;the other side has been closed.\n&quot;</span>);</span><br><span class="line"><span class="keyword">break</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;received from %s at PORT %d\n&quot;</span>,</span><br><span class="line">inet_ntop(AF_INET, &amp;(*ts).cliaddr.sin_addr, str, <span class="keyword">sizeof</span>(str)),</span><br><span class="line">ntohs((*ts).cliaddr.sin_port));</span><br><span class="line"><span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">buf[i] = <span class="built_in">toupper</span>(buf[i]);</span><br><span class="line">Write(ts-&gt;connfd, buf, n);</span><br><span class="line">&#125;</span><br><span class="line">Close(ts-&gt;connfd);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>, <span class="title">cliaddr</span>;</span></span><br><span class="line"><span class="type">socklen_t</span> cliaddr_len;</span><br><span class="line"><span class="type">int</span> listenfd, connfd;</span><br><span class="line"><span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="type">pthread_t</span> tid;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">s_info</span> <span class="title">ts</span>[256];</span></span><br><span class="line"></span><br><span class="line">listenfd = Socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">bzero(&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">servaddr.sin_addr.s_addr = htonl(INADDR_ANY);</span><br><span class="line">servaddr.sin_port = htons(SERV_PORT);</span><br><span class="line"></span><br><span class="line">Bind(listenfd, (<span class="keyword">struct</span> sockaddr *)&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">Listen(listenfd, <span class="number">20</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Accepting connections ...\n&quot;</span>);</span><br><span class="line"><span class="keyword">while</span> (<span class="number">1</span>) &#123;</span><br><span class="line">cliaddr_len = <span class="keyword">sizeof</span>(cliaddr);</span><br><span class="line">connfd = Accept(listenfd, (<span class="keyword">struct</span> sockaddr *)&amp;cliaddr, &amp;cliaddr_len);</span><br><span class="line">ts[i].cliaddr = cliaddr;</span><br><span class="line">ts[i].connfd = connfd;</span><br><span class="line"><span class="comment">/* 达到线程最大数时，pthread_create出错处理, 增加服务器稳定性 */</span></span><br><span class="line">pthread_create(&amp;tid, <span class="literal">NULL</span>, do_work, (<span class="type">void</span>*)&amp;ts[i]);</span><br><span class="line">i++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* client.c */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;netinet/in.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;wrap.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> MAXLINE 80</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SERV_PORT 6666</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sockaddr_in</span> <span class="title">servaddr</span>;</span></span><br><span class="line"><span class="type">char</span> buf[MAXLINE];</span><br><span class="line"><span class="type">int</span> sockfd, n;</span><br><span class="line"></span><br><span class="line">sockfd = Socket(AF_INET, SOCK_STREAM, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">bzero(&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line">servaddr.sin_family = AF_INET;</span><br><span class="line">inet_pton(AF_INET, <span class="string">&quot;127.0.0.1&quot;</span>, &amp;servaddr.sin_addr);</span><br><span class="line">servaddr.sin_port = htons(SERV_PORT);</span><br><span class="line"></span><br><span class="line">Connect(sockfd, (<span class="keyword">struct</span> sockaddr *)&amp;servaddr, <span class="keyword">sizeof</span>(servaddr));</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (fgets(buf, MAXLINE, <span class="built_in">stdin</span>) != <span class="literal">NULL</span>) &#123;</span><br><span class="line">Write(sockfd, buf, <span class="built_in">strlen</span>(buf));</span><br><span class="line">n = Read(sockfd, buf, MAXLINE);</span><br><span class="line"><span class="keyword">if</span> (n == <span class="number">0</span>)</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;the other side has been closed.\n&quot;</span>);</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">Write(STDOUT_FILENO, buf, n);</span><br><span class="line">&#125;</span><br><span class="line">Close(sockfd);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>##多路I&#x2F;O转接服务器</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;#socket&lt;br&gt;Socket本身有“插座”的意思，在Linux环境下，用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。&lt;/p&gt;
&lt;p&gt;在TCP&amp;#x2F;IP协议中，“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端</summary>
      
    
    
    
    
    <category term="linux网络编程" scheme="https://kettycode.github.io/tags/linux%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>atomic</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/atomic/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/atomic/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:16:40.721Z</updated>
    
    <content type="html"><![CDATA[<h1 id="std-atomic"><a href="#std-atomic" class="headerlink" title="std::atomic"></a>std::atomic</h1><h2 id="std-atomic-flag"><a href="#std-atomic-flag" class="headerlink" title="std::atomic_flag"></a>std::atomic_flag</h2><h3 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h3><p>std::atomic_flag 只有默认构造函数，拷贝构造函数已被禁用，因此不能从其他的 std::atomic_flag 对象构造一个新的 std::atomic_flag 对象。</p><p>如果在初始化时没有明确使用 ATOMIC_FLAG_INIT初始化，那么新创建的 std::atomic_flag 对象的状态是未指定的（unspecified）（既没有被 set 也没有被 clear。）另外，atomic_flag不能被拷贝，也不能 move 赋值。</p><p>ATOMIC_FLAG_INIT: 如果某个 std::atomic_flag 对象使用该宏初始化，那么可以保证该 std::atomic_flag 对象在创建时处于 clear 状态。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>              <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span>                <span class="comment">// std::atomic, std::atomic_flag, ATOMIC_FLAG_INIT</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>                <span class="comment">// std::thread, std::this_thread::yield</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span>                <span class="comment">// std::vector</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">std::atomic&lt;<span class="type">bool</span>&gt; <span class="title">ready</span><span class="params">(<span class="literal">false</span>)</span></span>;    <span class="comment">// can be checked without being set</span></span><br><span class="line">std::atomic_flag winner = ATOMIC_FLAG_INIT;    <span class="comment">// always set when checked</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">count1m</span><span class="params">(<span class="type">int</span> id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (!ready) &#123;</span><br><span class="line">        std::this_thread::<span class="built_in">yield</span>();</span><br><span class="line">    &#125; <span class="comment">// 等待主线程中设置 ready 为 true.</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000000</span>; ++i) &#123;</span><br><span class="line">    &#125; <span class="comment">// 计数.</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果某个线程率先执行完上面的计数过程，则输出自己的 ID.</span></span><br><span class="line">    <span class="comment">// 此后其他线程执行 test_and_set 是 if 语句判断为 false，</span></span><br><span class="line">    <span class="comment">// 因此不会输出自身 ID.</span></span><br><span class="line">    <span class="keyword">if</span> (!winner.<span class="built_in">test_and_set</span>()) &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;thread #&quot;</span> &lt;&lt; id &lt;&lt; <span class="string">&quot; won!\n&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::vector&lt;std::thread&gt; threads;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;spawning 10 threads that count to 1 million...\n&quot;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">10</span>; ++i)</span><br><span class="line">        threads.<span class="built_in">push_back</span>(std::<span class="built_in">thread</span>(count1m, i));</span><br><span class="line">    ready = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp; th:threads)</span><br><span class="line">        th.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="std-atomic-flag-text-and-set"><a href="#std-atomic-flag-text-and-set" class="headerlink" title="std::atomic_flag::text_and_set"></a>std::atomic_flag::text_and_set</h3><p>test_and_set() 函数检查 std::atomic_flag 标志，如果 std::atomic_flag 之前没有被设置过，则设置 std::atomic_flag 的标志，并返回先前该 std::atomic_flag 对象是否被设置过，如果之前 std::atomic_flag 对象已被设置，则返回 true，否则返回 false。</p><p>test-and-set 操作是原子的（因此 test-and-set 是原子 read-modify-write （RMW）操作）。</p><h3 id="std-atomic-flag-clear"><a href="#std-atomic-flag-clear" class="headerlink" title="std::atomic_flag::clear"></a>std::atomic_flag::clear</h3><p>清除 std::atomic_flag 对象的标志位，即设置 atomic_flag 的值为 false</p><h3 id="自旋锁"><a href="#自旋锁" class="headerlink" title="自旋锁"></a>自旋锁</h3><p>结合 std::atomic_flag::test_and_set() 和 std::atomic_flag::clear()，std::atomic_flag 对象可以当作一个简单的自旋锁使用</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::atomic_flag lock = ATOMIC_FLAG_INIT;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">f</span><span class="params">(<span class="type">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> cnt = <span class="number">0</span>; cnt &lt; <span class="number">100</span>; ++cnt) &#123;</span><br><span class="line">        <span class="keyword">while</span> (lock.<span class="built_in">test_and_set</span>(std::memory_order_acquire))  <span class="comment">// acquire lock</span></span><br><span class="line">             ; <span class="comment">// spin</span></span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Output from thread &quot;</span> &lt;&lt; n &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">        lock.<span class="built_in">clear</span>(std::memory_order_release);               <span class="comment">// release lock</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::vector&lt;std::thread&gt; v;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> n = <span class="number">0</span>; n &lt; <span class="number">10</span>; ++n) &#123;</span><br><span class="line">        v.<span class="built_in">emplace_back</span>(f, n);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t : v) &#123;</span><br><span class="line">        t.<span class="built_in">join</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在上面的程序中，std::atomic_flag 对象 lock 的上锁操作可以理解为 lock.test_and_set(std::memory_order_acquire); (此处指定了 Memory Order，更多有关 Memory Order 的概念，我会在后续的文章中介绍)，解锁操作相当与 lock.clear(std::memory_order_release)。</p><p>在上锁的时候，如果 lock.test_and_set 返回 false，则表示上锁成功（此时 while 不会进入自旋状态），因为此前 lock 的标志位为 false(即没有线程对 lock 进行上锁操作)，但调用 test_and_set 后 lock 的标志位为 true，说明某一线程已经成功获得了 lock 锁。</p><p>如果在该线程解锁（即调用 lock.clear(std::memory_order_release)） 之前，另外一个线程也调用 lock.test_and_set(std::memory_order_acquire) 试图获得锁，则 test_and_set(std::memory_order_acquire) 返回 true，则 while 进入自旋状态。如果获得锁的线程解锁（即调用了 lock.clear(std::memory_order_release)）之后，某个线程试图调用 lock.test_and_set(std::memory_order_acquire) 并且返回 false，则 while 不会进入自旋，此时表明该线程成功地获得了锁。</p><p>按照上面的分析，我们知道在某种情况下 std::atomic_flag 对象可以当作一个简单的自旋锁使用。</p><h2 id="std-atomic-1"><a href="#std-atomic-1" class="headerlink" title="std::atomic"></a>std::atomic</h2><p>std::atomic 是模板类，一个模板类型为 T 的原子对象中封装了一个类型为 T 的值:  template <class T> struct atomic;<br>原子类型对象的主要特点就是从不同线程访问不会导致数据竞争(data race)。因此从不同线程访问某个原子对象是良性 (well-defined) 行为，而通常对于非原子类型而言，并发访问某个对象（如果不做任何同步操作）会导致未定义 (undifined) 行为发生。</p><h3 id="构造函数-1"><a href="#构造函数-1" class="headerlink" title="构造函数"></a>构造函数</h3><p>默认构造函数，由默认构造函数创建的 std::atomic 对象处于未初始化(uninitialized)状态，对处于未初始化(uninitialized)状态 std::atomic对象可以由 atomic_init 函数进行初始化。<br>初始化构造函数，由类型 T初始化一个 std::atomic对象。<br>拷贝构造函数被禁用</p><h3 id="std-atomic-operator"><a href="#std-atomic-operator" class="headerlink" title="std::atomic::operator&#x3D;()"></a>std::atomic::operator&#x3D;()</h3><p>普通的赋值拷贝操作已经被禁用。但是一个类型为 T 的变量可以赋值给相应的原子类型变量（相当与隐式转换），该操作是原子的，内存序(Memory Order) 默认为顺序一致性(std::memory_order_seq_cst)，如果需要指定其他的内存序，需使用 std::atomic::store()。</p><h3 id="is-lock-free"><a href="#is-lock-free" class="headerlink" title="is_lock_free"></a>is_lock_free</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">is_lock_free</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">is_lock_free</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>判断该 std::atomic 对象是否具备 lock-free 的特性。如果某个对象满足 lock-free 特性，在多个线程访问该对象时不会导致线程阻塞。(可能使用某种事务内存transactional memory 方法实现 lock-free 的特性)。</p><h3 id="store"><a href="#store" class="headerlink" title="store"></a>store</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">store</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">store</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>修改被封装的值，std::atomic::store 函数将类型为 T 的参数 val 复制给原子对象所封装的值。T 是 std::atomic 类模板参数。另外参数 sync 指定内存序(Memory Order)</p><h3 id="load"><a href="#load" class="headerlink" title="load"></a>load</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">T <span class="title">load</span> <span class="params">(memory_order sync = memory_order_seq_cst)</span> <span class="type">const</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function">T <span class="title">load</span> <span class="params">(memory_order sync = memory_order_seq_cst)</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>读取被封装的值，参数 sync 设置内存序(Memory Order)</p><h3 id="operator-T"><a href="#operator-T" class="headerlink" title="operator T"></a>operator T</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">operator</span> <span class="title">T</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">operator</span> <span class="title">T</span><span class="params">()</span> <span class="type">const</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>与 load 功能类似，也是读取被封装的值，operator T() 是类型转换(type-cast)操作，默认的内存序是 std::memory_order_seq_cst</p><h3 id="exchange"><a href="#exchange" class="headerlink" title="exchange"></a>exchange</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">T <span class="title">exchange</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function">T <span class="title">exchange</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>读取并修改被封装的值，exchange 会将 val 指定的值替换掉之前该原子对象封装的值，并返回之前该原子对象封装的值，整个过程是原子的(因此exchange 操作也称为 read-modify-write 操作)。sync参数指定内存序(Memory Order)</p><h3 id="compare-exchange-weak"><a href="#compare-exchange-weak" class="headerlink" title="compare_exchange_weak"></a>compare_exchange_weak</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">compare_exchange_weak</span> <span class="params">(T&amp; expected, T val,</span></span></span><br><span class="line"><span class="params"><span class="function">           memory_order sync = memory_order_seq_cst)</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">compare_exchange_weak</span> <span class="params">(T&amp; expected, T val,</span></span></span><br><span class="line"><span class="params"><span class="function">           memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">compare_exchange_weak</span> <span class="params">(T&amp; expected, T val,</span></span></span><br><span class="line"><span class="params"><span class="function">           memory_order success, memory_order failure)</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">compare_exchange_weak</span> <span class="params">(T&amp; expected, T val,</span></span></span><br><span class="line"><span class="params"><span class="function">           memory_order success, memory_order failure)</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>比较并交换被封装的值(weak)与参数 expected 所指定的值是否相等，如果：<br>相等，则用 val 替换原子对象的旧值。<br>不相等，则用原子对象的旧值替换 expected ，因此调用该函数之后，如果被该原子对象封装的值与参数 expected 所指定的值不相等，expected 中的内容就是原子对象的旧值。<br>该函数通常会读取原子对象封装的值，如果比较为 true(即原子对象的值等于 expected)，则替换原子对象的旧值，但整个操作是原子的，在某个线程读取和修改该原子对象时，另外的线程不能对读取和修改该原子对象。</p><p>在第(2)种情况下，内存序（Memory Order）的选择取决于比较操作结果，如果比较结果为 true(即原子对象的值等于 expected)，则选择参数 success 指定的内存序，否则选择参数 failure 所指定的内存序。</p><p>注意，该函数直接比较原子对象所封装的值与参数 expected 的物理内容，所以某些情况下，对象的比较操作在使用 operator&#x3D;&#x3D;() 判断时相等，但 compare_exchange_weak 判断时却可能失败，因为对象底层的物理内容中可能存在位对齐或其他逻辑表示相同但是物理表示不同的值(比如 true 和 2 或 3，它们在逻辑上都表示”真”，但在物理上两者的表示并不相同)。</p><p>与compare_exchange_strong 不同, weak 版本的 compare-and-exchange 操作允许(spuriously 地)返回 false(即原子对象所封装的值与参数 expected 的物理内容相同，但却仍然返回 false)，不过在某些需要循环操作的算法下这是可以接受的，并且在一些平台下 compare_exchange_weak 的性能更好 。如果 compare_exchange_weak 的判断确实发生了伪失败(spurious failures)——即使原子对象所封装的值与参数 expected 的物理内容相同，但判断操作的结果却为 false，compare_exchange_weak函数返回 false，并且参数 expected 的值不会改变。</p><p>对于某些不需要采用循环操作的算法而言, 通常采用compare_exchange_strong 更好。</p><h2 id="std-atomic特化版本的成员函数"><a href="#std-atomic特化版本的成员函数" class="headerlink" title="std::atomic特化版本的成员函数"></a>std::atomic特化版本的成员函数</h2><h3 id="fetch-add"><a href="#fetch-add" class="headerlink" title="fetch_add"></a>fetch_add</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">if</span> T is <span class="title">integral</span> <span class="params">(<span class="number">1</span>)</span>:</span></span><br><span class="line"><span class="function">T fetch_add (T val, memory_order sync =</span> memory_order_seq_cst) <span class="keyword">volatile</span> <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="function">T <span class="title">fetch_add</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">if</span> T is <span class="title">pointer</span> <span class="params">(<span class="number">2</span>)</span>:</span></span><br><span class="line"><span class="function">T fetch_add (ptrdiff_t val, memory_order sync =</span> memory_order_seq_cst) <span class="keyword">volatile</span> <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="function">T <span class="title">fetch_add</span> <span class="params">(<span class="type">ptrdiff_t</span> val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>将原子对象的封装值加 val，并返回原子对象的旧值（适用于整形和指针类型的 std::atomic 特化版本），整个过程是原子的。另外，如果第二个参数不指定（取默认参数 memory_order_seq_cst），则 fetch_add 相当与 std::atomic::operator+&#x3D;。</p><h3 id="fetch-sub"><a href="#fetch-sub" class="headerlink" title="fetch_sub"></a>fetch_sub</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">if</span> T is <span class="title">integral</span> <span class="params">(<span class="number">1</span>)</span>:</span></span><br><span class="line"><span class="function">T fetch_sub (T val, memory_order sync =</span> memory_order_seq_cst) <span class="keyword">volatile</span> <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="function">T <span class="title">fetch_sub</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">if</span> T is <span class="title">pointer</span> <span class="params">(<span class="number">2</span>)</span>:</span></span><br><span class="line"><span class="function">T fetch_sub (ptrdiff_t val, memory_order sync =</span> memory_order_seq_cst) <span class="keyword">volatile</span> <span class="keyword">noexcept</span>;</span><br><span class="line"><span class="function">T <span class="title">fetch_sub</span> <span class="params">(<span class="type">ptrdiff_t</span> val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>将原子对象的封装值减 val，并返回原子对象的旧值（适用于整形和指针类型的 std::atomic 特化版本），整个过程是原子的,另外，如果第二个参数不指定（取默认参数 memory_order_seq_cst），则 fetch_sub 相当与 std::atomic::operator-&#x3D;。</p><h3 id="fetch-and"><a href="#fetch-and" class="headerlink" title="fetch_and"></a>fetch_and</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">T <span class="title">fetch_and</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function">T <span class="title">fetch_and</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>将原子对象的封装值按位与 val，并返回原子对象的旧值（只适用于整型的 std::atomic 特化版本），整个过程是原子的,另外，如果第二个参数不指定（取默认参数 memory_order_seq_cst），则 fetch_and 相当与 std::atomic::operator&amp;&#x3D;</p><h3 id="fetch-or"><a href="#fetch-or" class="headerlink" title="fetch_or"></a>fetch_or</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">T <span class="title">fetch_or</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">volatile</span> <span class="keyword">noexcept</span></span>;</span><br><span class="line"><span class="function">T <span class="title">fetch_or</span> <span class="params">(T val, memory_order sync = memory_order_seq_cst)</span> <span class="keyword">noexcept</span></span>;</span><br></pre></td></tr></table></figure><p>将原子对象的封装值按位或 val，并返回原子对象的旧值（只适用于整型的 std::atomic 特化版本），整个过程是原子的,另外，如果第二个参数不指定（取默认参数 memory_order_seq_cst），则 fetch_or 相当与 std::atomic::operator|&#x3D;</p><h3 id="fetch-xor"><a href="#fetch-xor" class="headerlink" title="fetch_xor"></a>fetch_xor</h3><h3 id="operator"><a href="#operator" class="headerlink" title="operator++"></a>operator++</h3><h3 id="operator–"><a href="#operator–" class="headerlink" title="operator–"></a>operator–</h3><h3 id="operator-comp-assign"><a href="#operator-comp-assign" class="headerlink" title="operator(comp.assign.)"></a>operator(comp.assign.)</h3>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;std-atomic&quot;&gt;&lt;a href=&quot;#std-atomic&quot; class=&quot;headerlink&quot; title=&quot;std::atomic&quot;&gt;&lt;/a&gt;std::atomic&lt;/h1&gt;&lt;h2 id=&quot;std-atomic-flag&quot;&gt;&lt;a href=&quot;#std-</summary>
      
    
    
    
    
    <category term="c++并发" scheme="https://kettycode.github.io/tags/c-%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>基础知识</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/base/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/base/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:16:49.917Z</updated>
    
    <content type="html"><![CDATA[<h1 id="并发编程基础知识"><a href="#并发编程基础知识" class="headerlink" title="并发编程基础知识"></a>并发编程基础知识</h1><h2 id="并发和并行"><a href="#并发和并行" class="headerlink" title="并发和并行"></a>并发和并行</h2><p>并发：单个cpu在同一时间段内，多个任务同时执行，偏向于多个任务交替执行，在某一时刻其实只有一个任务在执行（单个CPU就可并发，比如时间片轮转机制）。指单个cpu同时处理多个线程任务，cpu在反复切换任务线程，实际还是串行化的。<br>并行：同一时刻，多个任务同时执行（并行需要有多个CPU）。指多个cpu同时处理多个线程任务，cpu可以同时处理不同的任务，异步处理。</p><h2 id="进程和线程"><a href="#进程和线程" class="headerlink" title="进程和线程"></a>进程和线程</h2><p>进程，一个可执行程序运行起来就创建了一个进程。进程就是一个运行起来的可执行程序。<br>进程是一个程序及其数据在处理机上顺序执行时所发生的活动。<br>进程是具有独立功能的程序在一个数据集合上运行过程，它是系统进行资源分配和调度的一个独立单位。</p><p>在一个程序里的一个执行路线就叫做线程（thread）。更准确的定义是：线程是“一个进程内部的控制序列”。<br>一切进程至少都有一个执行线程。<br>线程在进程内部运行，本质是在进程地址空间内运行。<br>在Linux系统中，在CPU眼中，看到的PCB都要比传统的进程更加轻量化。<br>透过进程虚拟地址空间，可以看到进程的大部分资源，将进程资源合理分配给每个执行流，就形成了线程执行流。</p><h2 id="与多线程编程相关的头文件"><a href="#与多线程编程相关的头文件" class="headerlink" title="与多线程编程相关的头文件"></a>与多线程编程相关的头文件</h2><p><atomic>: std::atomic和std::atomic_flag<br><thread>: std::thread和std::this_thread<br><mutex>: std::mutex、std::lock_guard、std::unique_lock<br><condition_variable>: std::condition_variable、std::condition_variable_any<br><future>: std::promise、std::package_task、std::future、std::shared_future、std::async()</p><h1 id="简单的程序"><a href="#简单的程序" class="headerlink" title="简单的程序"></a>简单的程序</h1><h2 id="std-thread-“Hello-World”"><a href="#std-thread-“Hello-World”" class="headerlink" title="std::thread “Hello World”"></a>std::thread “Hello World”</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span> <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>   <span class="comment">// std::thread</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">thread_task</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;hello thread&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * ===  FUNCTION  =========================================================</span></span><br><span class="line"><span class="comment"> *         Name:  main</span></span><br><span class="line"><span class="comment"> *  Description:  program entry routine.</span></span><br><span class="line"><span class="comment"> * ========================================================================</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">const</span> <span class="type">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(thread_task)</span></span>;</span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> EXIT_SUCCESS;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;并发编程基础知识&quot;&gt;&lt;a href=&quot;#并发编程基础知识&quot; class=&quot;headerlink&quot; title=&quot;并发编程基础知识&quot;&gt;&lt;/a&gt;并发编程基础知识&lt;/h1&gt;&lt;h2 id=&quot;并发和并行&quot;&gt;&lt;a href=&quot;#并发和并行&quot; class=&quot;headerlink</summary>
      
    
    
    
    
    <category term="c++并发" scheme="https://kettycode.github.io/tags/c-%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>condition_variable</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/condition_variable/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/condition_variable/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:17:02.742Z</updated>
    
    <content type="html"><![CDATA[<h1 id="std-condition-variable"><a href="#std-condition-variable" class="headerlink" title="std::condition_variable"></a>std::condition_variable</h1><p>当 std::condition_variable 对象的某个 wait 函数被调用的时候，它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞，直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。</p><p>std::condition_variable 对象通常使用 std::unique_lock<a href="std::mutex">std::mutex</a> 来等待，如果需要使用另外的 lockable 类型，可以使用 std::condition_variable_any 类，本文后面会讲到 std::condition_variable_any 的用法。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>                <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>                <span class="comment">// std::thread</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span>                <span class="comment">// std::mutex, std::unique_lock</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;condition_variable&gt;</span>    <span class="comment">// std::condition_variable</span></span></span><br><span class="line"></span><br><span class="line">std::mutex mtx; <span class="comment">// 全局互斥锁.</span></span><br><span class="line">std::condition_variable cv; <span class="comment">// 全局条件变量.</span></span><br><span class="line"><span class="type">bool</span> ready = <span class="literal">false</span>; <span class="comment">// 全局标志位.</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">do_print_id</span><span class="params">(<span class="type">int</span> id)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::unique_lock &lt;std::mutex&gt; <span class="built_in">lck</span>(mtx);</span><br><span class="line">    <span class="keyword">while</span> (!ready) <span class="comment">// 如果标志位不为 true, 则等待...</span></span><br><span class="line">        cv.<span class="built_in">wait</span>(lck); <span class="comment">// 当前线程被阻塞, 当全局标志位变为 true 之后,</span></span><br><span class="line">    <span class="comment">// 线程被唤醒, 继续往下执行打印线程编号id.</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;thread &quot;</span> &lt;&lt; id &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">go</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::unique_lock &lt;std::mutex&gt; <span class="built_in">lck</span>(mtx);</span><br><span class="line">    ready = <span class="literal">true</span>; <span class="comment">// 设置全局标志位为 true.</span></span><br><span class="line">    cv.<span class="built_in">notify_all</span>(); <span class="comment">// 唤醒所有线程.</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::thread threads[<span class="number">10</span>];</span><br><span class="line">    <span class="comment">// spawn 10 threads:</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i)</span><br><span class="line">        threads[i] = std::<span class="built_in">thread</span>(do_print_id, i);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;10 threads ready to race...\n&quot;</span>;</span><br><span class="line">    <span class="built_in">go</span>(); <span class="comment">// go!</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp; th:threads)</span><br><span class="line">        th.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-condition-variable-构造函数"><a href="#std-condition-variable-构造函数" class="headerlink" title="std::condition_variable 构造函数"></a>std::condition_variable 构造函数</h2><p>std::condition_variable 的拷贝构造函数被禁用，只提供了默认构造函数</p><h2 id="std-condition-variable-wait"><a href="#std-condition-variable-wait" class="headerlink" title="std::condition_variable::wait"></a>std::condition_variable::wait</h2><p>1.void wait(unique_lock<mutex>&amp; lck);<br>2.template <typename Predicate> void wait (unique_lock<mutex>&amp; lck, Predicate pred);<br>td::condition_variable 提供了两种 wait() 函数。</p><p>第一种情况，当前线程调用 wait() 后将被阻塞(此时当前线程应该获得了锁（mutex），不妨设获得锁 lck)，直到另外某个线程调用 notify_* 唤醒了当前线程。</p><p>在线程被阻塞时，该函数会自动调用 lck.unlock() 释放锁，使得其他被阻塞在锁竞争上的线程得以继续执行。另外，一旦当前线程获得通知(notified，通常是另外某个线程调用 notify_* 唤醒了当前线程)，wait() 函数也是自动调用 lck.lock()，使得 lck 的状态和 wait 函数被调用时相同。</p><p>在第二种情况下（即设置了 Predicate），只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程，并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞。 类似while (!pred()) wait(lck);</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>                <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>                <span class="comment">// std::thread, std::this_thread::yield</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span>                <span class="comment">// std::mutex, std::unique_lock</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;condition_variable&gt;</span>    <span class="comment">// std::condition_variable</span></span></span><br><span class="line"></span><br><span class="line">std::mutex mtx;</span><br><span class="line">std::condition_variable cv;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> cargo = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">shipment_available</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> cargo != <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 消费者线程.</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">consume</span><span class="params">(<span class="type">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        std::unique_lock &lt;std::mutex&gt; <span class="built_in">lck</span>(mtx);</span><br><span class="line">        cv.<span class="built_in">wait</span>(lck, shipment_available);</span><br><span class="line">        std::cout &lt;&lt; cargo &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">        cargo = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">consumer_thread</span><span class="params">(consume, <span class="number">10</span>)</span></span>; <span class="comment">// 消费者线程.</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 主线程为生产者线程, 生产 10 个物品.</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; ++i) &#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">shipment_available</span>())</span><br><span class="line">            std::this_thread::<span class="built_in">yield</span>();</span><br><span class="line">        std::unique_lock &lt;std::mutex&gt; <span class="built_in">lck</span>(mtx);</span><br><span class="line">        cargo = i + <span class="number">1</span>;</span><br><span class="line">        cv.<span class="built_in">notify_one</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    consumer_thread.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-condition-variable-wait-for"><a href="#std-condition-variable-wait-for" class="headerlink" title="std::condition_variable::wait_for"></a>std::condition_variable::wait_for</h2><p>与std::condition_variable::wait类似</p><h2 id="std-condition-variable-wait-until"><a href="#std-condition-variable-wait-until" class="headerlink" title="std::condition_variable::wait_until"></a>std::condition_variable::wait_until</h2><p>与std::condition_variable::wait_until类似</p><h2 id="std-condition-variable-notify-one"><a href="#std-condition-variable-notify-one" class="headerlink" title="std::condition_variable::notify_one"></a>std::condition_variable::notify_one</h2><p>唤醒某个等待(wait)线程。如果当前没有等待线程，则该函数什么也不做，如果同时存在多个等待线程，则唤醒某个线程是不确定的(unspecified)。</p><h2 id="std-condition-variable-notify-all"><a href="#std-condition-variable-notify-all" class="headerlink" title="std::condition_variable::notify_all"></a>std::condition_variable::notify_all</h2><p>唤醒所有的等待(wait)线程。如果当前没有等待线程，则该函数什么也不做。</p><h2 id="std-condition-variable-any"><a href="#std-condition-variable-any" class="headerlink" title="std::condition_variable_any"></a>std::condition_variable_any</h2><p>与 std::condition_variable 类似，只不过 std::condition_variable_any 的 wait 函数可以接受任何 lockable 参数，而 std::condition_variable 只能接受 std::unique_lock<a href="std::mutex">std::mutex</a> 类型的参数，除此以外，和 std::condition_variable 几乎完全一样。</p><h2 id="std-cv-status枚举类"><a href="#std-cv-status枚举类" class="headerlink" title="std::cv_status枚举类"></a>std::cv_status枚举类</h2><p>cv_status::no_timeout:wait_for 或者 wait_until 没有超时，即在规定的时间段内线程收到了通知。<br>cv_status::timeout:wait_for 或者 wait_until 超时。</p><h2 id="std-notify-all-at-thread-exit"><a href="#std-notify-all-at-thread-exit" class="headerlink" title="std::notify_all_at_thread_exit"></a>std::notify_all_at_thread_exit</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>           <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>             <span class="comment">// std::thread</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span>              <span class="comment">// std::mutex, std::unique_lock</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;condition_variable&gt;</span> <span class="comment">// std::condition_variable</span></span></span><br><span class="line"></span><br><span class="line">std::mutex mtx;</span><br><span class="line">std::condition_variable cv;</span><br><span class="line"><span class="type">bool</span> ready = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print_id</span> <span class="params">(<span class="type">int</span> id)</span> </span>&#123;</span><br><span class="line">  <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lck</span><span class="params">(mtx)</span></span>;</span><br><span class="line">  <span class="keyword">while</span> (!ready) cv.<span class="built_in">wait</span>(lck);</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  std::cout &lt;&lt; <span class="string">&quot;thread &quot;</span> &lt;&lt; id &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">go</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lck</span><span class="params">(mtx)</span></span>;</span><br><span class="line">  std::<span class="built_in">notify_all_at_thread_exit</span>(cv,std::<span class="built_in">move</span>(lck));</span><br><span class="line">  ready = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  std::thread threads[<span class="number">10</span>];</span><br><span class="line">  <span class="comment">// spawn 10 threads:</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">10</span>; ++i)</span><br><span class="line">    threads[i] = std::<span class="built_in">thread</span>(print_id,i);</span><br><span class="line">  std::cout &lt;&lt; <span class="string">&quot;10 threads ready to race...\n&quot;</span>;</span><br><span class="line"></span><br><span class="line">  std::<span class="built_in">thread</span>(go).<span class="built_in">detach</span>();   <span class="comment">// go!</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; th : threads) th.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;std-condition-variable&quot;&gt;&lt;a href=&quot;#std-condition-variable&quot; class=&quot;headerlink&quot; title=&quot;std::condition_variable&quot;&gt;&lt;/a&gt;std::condition_vari</summary>
      
    
    
    
    
    <category term="c++并发" scheme="https://kettycode.github.io/tags/c-%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>future</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/future/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/future/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:17:11.411Z</updated>
    
    <content type="html"><![CDATA[<h1 id="std-future"><a href="#std-future" class="headerlink" title="std::future"></a>std::future</h1><h2 id="std-promise类"><a href="#std-promise类" class="headerlink" title="std::promise类"></a>std::promise类</h2><p>promise 对象可以保存某一类型 T 的值，该值可被 future 对象读取（可能在另外一个线程中），因此 promise 也提供了一种线程同步的手段。在 promise 对象构造时可以和一个共享状态（通常是std::future）相关联，并可以在相关联的共享状态(std::future)上保存一个类型为 T 的值。</p><p>可以通过 get_future 来获取与该 promise 对象相关联的 future 对象，调用该函数之后，两个对象共享相同的共享状态(shared state)</p><p>promise 对象是异步 Provider，它可以在某一时刻设置共享状态的值。<br>future 对象可以异步返回共享状态的值，或者在必要的情况下阻塞调用者并等待共享状态标志变为 ready，然后才能获取共享状态的值</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>       <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span>     <span class="comment">// std::ref</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>         <span class="comment">// std::thread</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span>         <span class="comment">// std::promise, std::future</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print_int</span><span class="params">(std::future&lt;<span class="type">int</span>&gt;&amp; fut)</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> x = fut.<span class="built_in">get</span>(); <span class="comment">// 获取共享状态的值.</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;value: &quot;</span> &lt;&lt; x &lt;&lt; <span class="string">&#x27;\n&#x27;</span>; <span class="comment">// 打印 value: 10.</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::promise&lt;<span class="type">int</span>&gt; prom; <span class="comment">// 生成一个 std::promise&lt;int&gt; 对象.</span></span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt; fut = prom.<span class="built_in">get_future</span>(); <span class="comment">// 和 future 关联.</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t</span><span class="params">(print_int, std::ref(fut))</span></span>; <span class="comment">// 将 future 交给另外一个线程t.</span></span><br><span class="line">    prom.<span class="built_in">set_value</span>(<span class="number">10</span>); <span class="comment">// 设置共享状态的值, 此处和线程t保持同步.</span></span><br><span class="line">    t.<span class="built_in">join</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-promise构造函数"><a href="#std-promise构造函数" class="headerlink" title="std::promise构造函数"></a>std::promise构造函数</h2><p>默认构造函数，初始化一个空的共享状态。<br>带自定义内存分配器的构造函数，与默认构造函数类似，但是使用自定义分配器来分配共享状态。<br>拷贝构造函数，被禁用。<br>移动构造函数。</p><p>另外，std::promise 的 operator&#x3D; 没有拷贝语义，即 std::promise 普通的赋值操作被禁用，operator&#x3D; 只有 move 语义，所以 std::promise 对象是禁止拷贝的</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>       <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>         <span class="comment">// std::thread</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span>         <span class="comment">// std::promise, std::future</span></span></span><br><span class="line"></span><br><span class="line">std::promise&lt;<span class="type">int</span>&gt; prom;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print_global_promise</span> <span class="params">()</span> </span>&#123;</span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt; fut = prom.<span class="built_in">get_future</span>();</span><br><span class="line">    <span class="type">int</span> x = fut.<span class="built_in">get</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;value: &quot;</span> &lt;&lt; x &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">th1</span><span class="params">(print_global_promise)</span></span>;</span><br><span class="line">    prom.<span class="built_in">set_value</span>(<span class="number">10</span>);</span><br><span class="line">    th1.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    prom = std::<span class="built_in">promise</span>&lt;<span class="type">int</span>&gt;();    <span class="comment">// prom 被move赋值为一个新的 promise 对象.</span></span><br><span class="line"></span><br><span class="line">    <span class="function">std::thread <span class="title">th2</span> <span class="params">(print_global_promise)</span></span>;</span><br><span class="line">    prom.<span class="built_in">set_value</span> (<span class="number">20</span>);</span><br><span class="line">    th2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-packaged-task类"><a href="#std-packaged-task类" class="headerlink" title="std::packaged_task类"></a>std::packaged_task类</h2><p>std::packaged_task 包装一个可调用的对象，并且允许异步获取该可调用对象产生的结果，从包装可调用对象意义上来讲，std::packaged_task 与 std::function 类似，只不过 std::packaged_task 将其包装的可调用对象的执行结果传递给一个 std::future 对象（该对象通常在另外一个线程中获取 std::packaged_task 任务的执行结果）。</p><p>std::packaged_task 对象内部包含了两个最基本元素，一、被包装的任务(stored task)，任务(task)是一个可调用的对象，如函数指针、成员函数指针或者函数对象，二、共享状态(shared state)，用于保存任务的返回值，可以通过 std::future 对象来达到异步访问共享状态的效果。</p><p>可以通过 std::packged_task::get_future 来获取与共享状态相关联的 std::future 对象。在调用该函数之后，两个对象共享相同的共享状态，具体解释如下：</p><p>std::packaged_task 对象是异步 Provider，它在某一时刻通过调用被包装的任务来设置共享状态的值。<br>std::future 对象是一个异步返回对象，通过它可以获得共享状态的值，当然在必要的时候需要等待共享状态标志变为 ready.<br>std::packaged_task 的共享状态的生命周期一直持续到最后一个与之相关联的对象被释放或者销毁为止。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>     <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span>       <span class="comment">// std::packaged_task, std::future</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span>       <span class="comment">// std::chrono::seconds</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>       <span class="comment">// std::thread, std::this_thread::sleep_for</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// count down taking a second for each value:</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">countdown</span> <span class="params">(<span class="type">int</span> from, <span class="type">int</span> to)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i=from; i!=to; --i) &#123;</span><br><span class="line">        std::cout &lt;&lt; i &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(<span class="number">1</span>));</span><br><span class="line">    &#125;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Finished!\n&quot;</span>;</span><br><span class="line">    <span class="keyword">return</span> from - to;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="type">int</span>,<span class="type">int</span>)</span>&gt; <span class="title">task</span><span class="params">(countdown)</span></span>; <span class="comment">// 设置 packaged_task</span></span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt; ret = task.<span class="built_in">get_future</span>(); <span class="comment">// 获得与 packaged_task 共享状态相关联的 future 对象.</span></span><br><span class="line"></span><br><span class="line">    <span class="function">std::thread <span class="title">th</span><span class="params">(std::move(task), <span class="number">10</span>, <span class="number">0</span>)</span></span>;   <span class="comment">//创建一个新线程完成计数任务.</span></span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> value = ret.<span class="built_in">get</span>();                    <span class="comment">// 等待任务完成并获取结果.</span></span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;The countdown lasted for &quot;</span> &lt;&lt; value &lt;&lt; <span class="string">&quot; seconds.\n&quot;</span>;</span><br><span class="line"></span><br><span class="line">    th.<span class="built_in">join</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-packaged-task构造函数"><a href="#std-packaged-task构造函数" class="headerlink" title="std::packaged_task构造函数"></a>std::packaged_task构造函数</h2><p>std::packaged_task 构造函数共有 5 中形式，不过拷贝构造已经被禁用了。下面简单地介绍一下几种构造函数的语义：</p><p>默认构造函数，初始化一个空的共享状态，并且该 packaged_task 对象无包装任务。<br>初始化一个共享状态，并且被包装任务由参数 fn 指定。<br>带自定义内存分配器的构造函数，与默认构造函数类似，但是使用自定义分配器来分配共享状态。<br>拷贝构造函数，被禁用。<br>移动构造函数</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>     <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;utility&gt;</span>      <span class="comment">// std::move</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span>       <span class="comment">// std::packaged_task, std::future</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>       <span class="comment">// std::thread</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::packaged_task&lt;<span class="built_in">int</span>(<span class="type">int</span>)&gt; foo; <span class="comment">// 默认构造函数.</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用 lambda 表达式初始化一个 packaged_task 对象.</span></span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="type">int</span>)</span>&gt; <span class="title">bar</span><span class="params">([](<span class="type">int</span> x)&#123;<span class="keyword">return</span> x*<span class="number">2</span>;&#125;)</span></span>;</span><br><span class="line"></span><br><span class="line">    foo = std::<span class="built_in">move</span>(bar); <span class="comment">// move-赋值操作，也是 C++11 中的新特性.</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取与 packaged_task 共享状态相关联的 future 对象.</span></span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt; ret = foo.<span class="built_in">get_future</span>();</span><br><span class="line"></span><br><span class="line">    std::<span class="built_in">thread</span>(std::<span class="built_in">move</span>(foo), <span class="number">10</span>).<span class="built_in">detach</span>(); <span class="comment">// 产生线程，调用被包装的任务.</span></span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> value = ret.<span class="built_in">get</span>(); <span class="comment">// 等待任务完成并获取结果.</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;The double of 10 is &quot;</span> &lt;&lt; value &lt;&lt; <span class="string">&quot;.\n&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-packaged-task-valid"><a href="#std-packaged-task-valid" class="headerlink" title="std::packaged_task::valid"></a>std::packaged_task::valid</h2><p>检查当前 packaged_task 是否和一个有效的共享状态相关联，对于由默认构造函数生成的 packaged_task 对象，该函数返回 false，除非中间进行了 move 赋值操作或者 swap 操作</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>     <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;utility&gt;</span>      <span class="comment">// std::move</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span>       <span class="comment">// std::packaged_task, std::future</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>       <span class="comment">// std::thread</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 在新线程中启动一个 int(int) packaged_task.</span></span><br><span class="line"><span class="function">std::future&lt;<span class="type">int</span>&gt; <span class="title">launcher</span><span class="params">(std::packaged_task&lt;<span class="type">int</span>(<span class="type">int</span>)&gt;&amp; tsk, <span class="type">int</span> arg)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tsk.<span class="built_in">valid</span>()) &#123;</span><br><span class="line">        std::future&lt;<span class="type">int</span>&gt; ret = tsk.<span class="built_in">get_future</span>();</span><br><span class="line">        std::<span class="built_in">thread</span> (std::<span class="built_in">move</span>(tsk),arg).<span class="built_in">detach</span>();</span><br><span class="line">        <span class="keyword">return</span> ret;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">return</span> std::<span class="built_in">future</span>&lt;<span class="type">int</span>&gt;();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">int</span><span class="params">(<span class="type">int</span>)</span>&gt; <span class="title">tsk</span><span class="params">([](<span class="type">int</span> x)&#123;<span class="keyword">return</span> x*<span class="number">2</span>;&#125;)</span></span>;</span><br><span class="line"></span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt; fut = <span class="built_in">launcher</span>(tsk,<span class="number">25</span>);</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;The double of 25 is &quot;</span> &lt;&lt; fut.<span class="built_in">get</span>() &lt;&lt; <span class="string">&quot;.\n&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-packaged-task-reset"><a href="#std-packaged-task-reset" class="headerlink" title="std::packaged_task::reset"></a>std::packaged_task::reset</h2><p>重置 packaged_task 的共享状态，但是保留之前的被包装的任务</p><h2 id="std-packaged-task-swap"><a href="#std-packaged-task-swap" class="headerlink" title="std::packaged_task::swap"></a>std::packaged_task::swap</h2><p>交换 packaged_task 的共享状态</p><h2 id="std-future-类"><a href="#std-future-类" class="headerlink" title="std::future 类"></a>std::future 类</h2><p>std::future 究竟是什么呢？简单地说，std::future 可以用来获取异步任务的结果，因此可以把它当成一种简单的线程间同步的手段。std::future 通常由某个 Provider 创建，你可以把 Provider 想象成一个异步任务的提供者，Provider 在某个线程中设置共享状态的值，与该共享状态相关联的 std::future 对象调用 get（通常在另外一个线程中） 获取该值，如果共享状态的标志不为 ready，则调用 std::future::get 会阻塞当前的调用者，直到 Provider 设置了共享状态的值（此时共享状态的标志变为 ready），std::future::get 返回异步任务的值或异常（如果发生了异常）。</p><p>一个有效(valid)的 std::future 对象通常由以下三种 Provider 创建，并和某个共享状态相关联。Provider 可以是函数或者类，其实我们前面都已经提到了，他们分别是：</p><p>std::async 函数，本文后面会介绍 std::async() 函数。<br>std::promise::get_future，get_future 为 promise 类的成员函数<br>std::packaged_task::get_future， get_future为 packaged_task 的成员函数<br>一个 std::future 对象只有在有效(valid)的情况下才有用(useful)，由 std::future 默认构造函数创建的 future 对象不是有效的（除非当前非有效的 future 对象被 move 赋值另一个有效的 future 对象）。</p><p> 在一个有效的 future 对象上调用 get 会阻塞当前的调用者，直到 Provider 设置了共享状态的值或异常（此时共享状态的标志变为 ready），std::future::get 将返回异步任务的值或异常（如果发生了异常）。</p><h2 id="std-async"><a href="#std-async" class="headerlink" title="std::async"></a>std::async</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// future example</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>             <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span>               <span class="comment">// std::async, std::future</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span>               <span class="comment">// std::chrono::milliseconds</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// a non-optimized way of checking for prime numbers:</span></span><br><span class="line"><span class="function"><span class="type">bool</span></span></span><br><span class="line"><span class="function"><span class="title">is_prime</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt; x; ++i)</span><br><span class="line">        <span class="keyword">if</span> (x % i == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span></span></span><br><span class="line"><span class="function"><span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// call function asynchronously:</span></span><br><span class="line">    std::future &lt; <span class="type">bool</span> &gt; fut = std::<span class="built_in">async</span>(is_prime, <span class="number">444444443</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// do something while waiting for function to set future:</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;checking, please wait&quot;</span>;</span><br><span class="line">    std::<span class="function">chrono::milliseconds <span class="title">span</span><span class="params">(<span class="number">100</span>)</span></span>;</span><br><span class="line">    <span class="keyword">while</span> (fut.<span class="built_in">wait_for</span>(span) == std::future_status::timeout)</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&#x27;.&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">bool</span> x = fut.<span class="built_in">get</span>();         <span class="comment">// retrieve return value</span></span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;\n444444443 &quot;</span> &lt;&lt; (x ? <span class="string">&quot;is&quot;</span> : <span class="string">&quot;is not&quot;</span>) &lt;&lt; <span class="string">&quot; prime.\n&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-future构造函数"><a href="#std-future构造函数" class="headerlink" title="std::future构造函数"></a>std::future构造函数</h2><p>std::future 的拷贝构造函数是被禁用的，只提供了默认的构造函数和 move 构造函数。另外，std::future 的普通赋值操作也被禁用，只提供了 move 赋值操作</p><h2 id="std-future-share"><a href="#std-future-share" class="headerlink" title="std::future::share"></a>std::future::share</h2><p>返回一个 std::shared_future 对象，调用该函数之后，该 std::future 对象本身已经不和任何共享状态相关联，因此该 std::future 的状态不再是 valid 的了。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>       <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;future&gt;</span>         <span class="comment">// std::async, std::future, std::shared_future</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">do_get_value</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> <span class="number">10</span>; &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::future&lt;<span class="type">int</span>&gt; fut = std::<span class="built_in">async</span>(do_get_value);</span><br><span class="line">    std::shared_future&lt;<span class="type">int</span>&gt; shared_fut = fut.<span class="built_in">share</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 共享的 future 对象可以被多次访问.</span></span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;value: &quot;</span> &lt;&lt; shared_fut.<span class="built_in">get</span>() &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;its double: &quot;</span> &lt;&lt; shared_fut.<span class="built_in">get</span>()*<span class="number">2</span> &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-future-get"><a href="#std-future-get" class="headerlink" title="std::future::get"></a>std::future::get</h2><p>当与该 std::future 对象相关联的共享状态标志变为 ready 后，调用该函数将返回保存在共享状态中的值，如果共享状态的标志不为 ready，则调用该函数会阻塞当前的调用者，而此后一旦共享状态的标志变为 ready，get 返回 Provider 所设置的共享状态的值或者异常（如果抛出了异常）</p><h2 id="std-future-valid"><a href="#std-future-valid" class="headerlink" title="std::future::valid"></a>std::future::valid</h2><p>检查当前的 std::future 对象是否有效，即释放与某个共享状态相关联。一个有效的 std::future 对象只能通过 std::async(), std::future::get_future 或者 std::packaged_task::get_future 来初始化。</p><h2 id="std-future-wait"><a href="#std-future-wait" class="headerlink" title="std::future::wait"></a>std::future::wait</h2><p>等待与当前std::future 对象相关联的共享状态的标志变为 ready.</p><p>如果共享状态的标志不是 ready（此时 Provider 没有在共享状态上设置值（或者异常）），调用该函数会被阻塞当前线程，直到共享状态的标志变为 ready。<br>一旦共享状态的标志变为 ready，wait() 函数返回，当前线程被解除阻塞，但是 wait() 并不读取共享状态的值或者异常。</p><h2 id="std-future-wait-for"><a href="#std-future-wait-for" class="headerlink" title="std::future::wait_for"></a>std::future::wait_for</h2><p>与 std::future::wait() 的功能类似，即等待与该 std::future 对象相关联的共享状态的标志变为 ready，而与 std::future::wait() 不同的是，wait_for() 可以设置一个时间段 rel_time，如果共享状态的标志在该时间段结束之前没有被 Provider 设置为 ready，则调用 wait_for 的线程被阻塞，在等待了 rel_time 的时间长度后 wait_for() 返回(future_status::ready、future_status::timeout、future_status::deferred)</p><h2 id="std-future-wait-until"><a href="#std-future-wait-until" class="headerlink" title="std::future::wait_until"></a>std::future::wait_until</h2><p>与std::future::wait_for类似</p><h2 id="std-shared-future类"><a href="#std-shared-future类" class="headerlink" title="std::shared_future类"></a>std::shared_future类</h2><p>std::shared_future 与 std::future 类似，但是 std::shared_future 可以拷贝、多个 std::shared_future 可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。shared_future 可以通过某个 std::future 对象隐式转换（参见 std::shared_future 的构造函数），或者通过 std::future::share() 显示转换，无论哪种转换，被转换的那个 std::future 对象都会变为 not-valid.</p><h2 id="std-future-error类"><a href="#std-future-error类" class="headerlink" title="std::future_error类"></a>std::future_error类</h2><p>std::future_error 继承子 C++ 标准异常体系中的 logic_error</p><h2 id="std-future枚举类"><a href="#std-future枚举类" class="headerlink" title="std::future枚举类"></a>std::future枚举类</h2><p>enum class future_errc;<br>enum class future_status;<br>enum class launch;<br>下面分别介绍以上三种枚举类型：</p><p>std::future_errc 类型描述如下（参考）：<br>类型                       取值                   描述<br>broken_promise            0与该 std::future 共享状态相关联的 std::promise 对象在设置值或者异常之前一被销毁。<br>future_already_retrieved1与该 std::future 对象相关联的共享状态的值已经被当前 Provider 获取了，即调用了 std::future::get 函数。<br>promise_already_satisfied2std::promise 对象已经对共享状态设置了某一值或者异常。<br>no_state                3无共享状态。</p><p>std::future_status 类型主要用在 std::future(或std::shared_future)中的 wait_for 和 wait_until 两个函数中的。<br>类型               取值                 描述<br>future_status::ready0wait_for(或wait_until) 因为共享状态的标志变为 ready 而返回。<br>future_status::timeout1超时，即 wait_for(或wait_until) 因为在指定的时间段（或时刻）内共享状态的标志依然没有变为 ready 而返回。<br>future_status::deferred2共享状态包含了 deferred 函数。</p><p>std::launch 类型，该枚举类型主要是在调用 std::async 设置异步任务的启动策略的。</p><p>类型                      描述<br>launch::async    Asynchronous: 异步任务会在另外一个线程中调用，并通过共享状态返回异步任务的结果（一般是调用 std::future::get() 获取异步任务的结果）。<br>launch::deferredDeferred: 异步任务将会在共享状态被访问时调用，相当与按需调用（即延迟(deferred)调用）。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;std-future&quot;&gt;&lt;a href=&quot;#std-future&quot; class=&quot;headerlink&quot; title=&quot;std::future&quot;&gt;&lt;/a&gt;std::future&lt;/h1&gt;&lt;h2 id=&quot;std-promise类&quot;&gt;&lt;a href=&quot;#std-pro</summary>
      
    
    
    
    
    <category term="c++并发" scheme="https://kettycode.github.io/tags/c-%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>第一部分</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/template1/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/template1/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:17:58.611Z</updated>
    
    <content type="html"><![CDATA[<p>注意：模板学习内容均参考 <a href="https://github.com/actuatorpro/CppTemplateTutorial.git">https://github.com/actuatorpro/CppTemplateTutorial.git</a>, 可自主去github上学习</p><h1 id="1-前言"><a href="#1-前言" class="headerlink" title="1. 前言"></a>1. 前言</h1><h2 id="1-1-C-另类简介：比你用的复杂，但比你想的简单"><a href="#1-1-C-另类简介：比你用的复杂，但比你想的简单" class="headerlink" title="1.1. C++另类简介：比你用的复杂，但比你想的简单"></a>1.1. C++另类简介：比你用的复杂，但比你想的简单</h2><p>C++似乎从它为世人所知的那天开始便成为天然的话题性编程语言。在它在周围有着形形色色的赞美与贬低之词。当我在微博上透露欲写此文的意愿时，也收到了很多褒贬不一的评论。作为一门语言，能拥有这么多使用并恨着它、使用并畏惧它的用户，也算是语言丛林里的奇观了。</p><p>C++之所以变成一门层次丰富、结构多变、语法繁冗的语言，是有着多层次的原因的。Bjarne在《The Design and Evolution of C++》一书中，详细的解释了C++为什么会变成如今（C++98&#x2F;03）的模样。这本书也是我和陈梓瀚一直对各位已经入门的新手强烈推荐的一本书。通过它你多少可以明白，C++的诸多语法要素之所以变成如今的模样，实属迫不得已。</p><p>模板作为C++中最有特色的语言特性，它堪称玄学的语法和语义，理所应当的成为初学者的梦魇。甚至很多工作多年的人也对C++的模板部分保有充分的敬畏。在多数的编码标准中，Template俨然和多重继承一样，成为了一般程序员（非程序库撰写者）的禁区。甚至运用模板较多的Boost，也成为了“众矢之的”。</p><p>但是实际上C++模板远没有想象的那么复杂。我们只需要换一个视角：在C++03的时候，模板本身就可以独立成为一门“语言”。它有“值”，有“函数”，有“表达式”和“语句”。除了语法比较蹩脚外，它既没有指针也没有数组，更没有C++里面复杂的继承和多态。可以说，它要比C语言要简单的多。如果我们把模板当做是一门语言来学习，那只需要花费学习OO零头的时间即可掌握。按照这样的思路，可以说在各种模板书籍中出现的多数技巧，都可以被轻松理解。</p><p>简单回顾一下模板的历史。87年的时候，泛型（Generic Programming）便被纳入了C++的考虑范畴，并直接导致了后来模板语法的产生。可以说模板语法一开始就是为了在C++中提供泛型机制。92年的时候，Alexander Stepanov开始研究利用模板语法制作程序库，后来这一程序库发展成STL，并在93年被接纳入标准中。</p><p>此时不少人以为STL已经是C++模板的集大成之作，C++模板技止于此。但是在95年的《C++ Report》上，John Barton和Lee Nackman提出了一个矩阵乘法的模板示例。可以说元编程在那个时候开始被很多人所关注。自此篇文章发表之后，很多大牛都开始对模板产生了浓厚的兴趣。其中对元编程技法贡献最大的当属Alexandrescu的《Modern C++ Design》及模板程序库Loki。这一2001年发表的图书间接地导致了模板元编程库的出现。书中所使用的Typelist等泛型组件，和Policy等设计方法令人耳目一新。但是因为全书用的是近乎Geek的手法来构造一切设施，因此使得此书阅读起来略有难度。</p><p>2002年出版的另一本书《C++ Templates》，可以说是在Template方面的集大成之作。它详细阐述了模板的语法、提供了和模板有关的语言细节信息，举了很多有代表性例子。但是对于模板新手来说，这本书细节如此丰富，让他们随随便便就打了退堂鼓缴械投降。</p><p>本文的写作初衷，就是通过“编程语言”的视角，介绍一个简单、清晰的“模板语言”。我会尽可能地将模板的诸多要素连串起来，用一些简单的例子帮助读者学习这门“语言”，让读者在编写、阅读模板代码的时候，能像 <code>if(exp) &#123; dosomething(); &#125;</code>一样的信手拈来，让“模板元编程”技术成为读者牢固掌握、可举一反三的有用技能。</p><h2 id="1-2-适宜读者群"><a href="#1-2-适宜读者群" class="headerlink" title="1.2. 适宜读者群"></a>1.2. 适宜读者群</h2><p>因为本文并不是用于C++入门，例子中也多少会牵涉一些其它知识，因此如果读者能够具备以下条件，会读起来更加轻松：</p><ul><li>熟悉C++的基本语法；</li><li>使用过STL；</li><li>熟悉一些常用的算法，以及递归等程序设计方法。</li></ul><p>此外，尽管第一章会介绍一些Template的基本语法，但是还是会略显单薄。因此也希望读者能对C++ Template最基本语法形式有所了解和掌握；如果会编写基本的函数模板和类模板那就更好了。</p><p>诚如上节所述，本文并不是《C++ Templates》的简单重复，与《Modern C++ Design》交叠更少。从知识结构上，我建议大家可以先读本文，再阅读《C++ Templates》获取更丰富的语法与实现细节，以更进一步；《Modern C++ Design》除了元编程之外，还有很多的泛型编程示例，原则上泛型编程的部分与我所述的内容交叉不大，读者在读完1-3章了解模板的基本规则之后便可阅读《MCD》的相应章节；元编程部分（如Typelist）建议在阅读完本文之后再行阅读，或许会更易理解。</p><h2 id="1-3-版权"><a href="#1-3-版权" class="headerlink" title="1.3. 版权"></a>1.3. 版权</h2><p>本文是随写随即同步到Github上，因此在行文中难免会遗漏引用。本文绝大部分内容应是直接承出我笔，但是也不定会有他山之石。所有指涉内容我会尽量以引号框记，或在上下文和边角注记中标示，如有遗漏烦请不吝指出。</p><p>全文所有为我所撰写的部分，作者均保留所有版权。如果有需要转帖或引用，还请注明出处并告知于我。</p><h2 id="1-4-推荐编译环境"><a href="#1-4-推荐编译环境" class="headerlink" title="1.4. 推荐编译环境"></a>1.4. 推荐编译环境</h2><p>C++编译器众多，且对模板的支持可能存在细微差别。如果没有特别强调，本书行文过程中，使用了下列编译器来测试文中提供的代码和示例：</p><ul><li>Clang 14.0.3; 15.0 (amd64)</li><li>Visual Studio 2022 19.2+ (amd64)</li></ul><p>此外，部分复杂实例我们还在文中提供了在线的编译器预览以方便大家阅读和测试。在线编译器参见： <a href="https://gcc.godbolt.org/"><code>gcc.godbolt.org</code></a>。</p><p>一些示例中用到的特性所对应的C++标准：</p><table><thead><tr><th>特性</th><th>标准</th></tr></thead><tbody><tr><td>std::decay_t<T></td><td>C++ 14</td></tr></tbody></table><h2 id="1-5-体例"><a href="#1-5-体例" class="headerlink" title="1.5. 体例"></a>1.5. 体例</h2><h3 id="1-5-1-示例代码"><a href="#1-5-1-示例代码" class="headerlink" title="1.5.1. 示例代码"></a>1.5.1. 示例代码</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">SampleCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 这是一段示例代码</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="1-5-2-引用"><a href="#1-5-2-引用" class="headerlink" title="1.5.2. 引用"></a>1.5.2. 引用</h3><p>引用自C++标准：</p><blockquote><p>1.1.2&#x2F;1 这是一段引用或翻译自标准的文字</p></blockquote><p>引用自其他图书：</p><blockquote><p>《书名》<br>这是一段引用或翻译自其他图书的文字</p></blockquote><h2 id="1-6-意见、建议、喷、补遗、写作计划"><a href="#1-6-意见、建议、喷、补遗、写作计划" class="headerlink" title="1.6. 意见、建议、喷、补遗、写作计划"></a>1.6. 意见、建议、喷、补遗、写作计划</h2><ul><li>需增加：<ul><li>模板的使用动机。</li><li>增加“如何使用本文”一节。本节将说明全书的体例（强调字体、提示语、例子的组织），所有的描述、举例、引用在重审时将按照体例要求重新组织。</li><li>除了用于描述语法的例子外，其他例子将尽量赋予实际意义，以方便阐述意图。</li><li>在合适的章节完整叙述模板的类型推导规则。Parameter-Argument, auto variable, decltype, decltype(auto)</li><li>在函数模板重载和实例化的部分讲述ADL。</li><li>变参模板处应当按照标准（Argument Packing&#x2F;Unpacking）来讲解。</li></ul></li><li>建议：<ul><li>比较模板和函数的差异性</li><li>蓝色：C++14 Return type deduction for normal functions 的分析</li></ul></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;注意：模板学习内容均参考 &lt;a href=&quot;https://github.com/actuatorpro/CppTemplateTutorial.git&quot;&gt;https://github.com/actuatorpro/CppTemplateTutorial.git&lt;/a&gt;,</summary>
      
    
    
    
    
    <category term="c++ template" scheme="https://kettycode.github.io/tags/c-template/"/>
    
  </entry>
  
  <entry>
    <title>memory</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/memory/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/memory/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:17:18.663Z</updated>
    
    <content type="html"><![CDATA[<h1 id="内存模型"><a href="#内存模型" class="headerlink" title="内存模型"></a>内存模型</h1><p>原子类型的大多数 API 都需要程序员提供一个 std::memory_order（可译为内存序，访存顺序） 的枚举类型值作为参数，比如：atomic_store，atomic_load，atomic_exchange，atomic_compare_exchange 等 API 的最后一个形参为 std::memory_order order，默认值是 std::memory_order_seq_cst（顺序一致性）。那么究竟什么是 std::memory_order 呢，为了解答这个问题，我们先来讨论 C++11 的内存模型。</p><p>一般来讲，内存模型可分为静态内存模型和动态内存模型，静态内存模型主要涉及类的对象在内存中是如何存放的，即从结构(structural)方面来看一个对象在内存中的布局。动态内存模型可理解为存储一致性模型，主要是从行为(behavioral)方面来看多个线程对同一个对象同时(读写)操作时(concurrency)所做的约束，动态内存模型理解起来稍微复杂一些，涉及了内存，Cache，CPU 各个层次的交互，尤其是在共享存储系统中，为了保证程序执行的正确性，就需要对访存事件施加严格的限制。</p><p>std::memory_order 规定了普通访存操作和相邻的原子访存操作之间的次序是如何安排的，在多核系统中，当多个线程同时读写多个变量时，其中的某个线程所看到的变量值的改变顺序可能和其他线程写入变量值的次序不相同。同时，不同的线程所观察到的某变量被修改次序也可能不相同。然而，如果保证所有对原子变量的操作都是顺序的话，可能对程序的性能影响很大，因此，我们可以通过 std::memory_order 来指定编译器对访存次序所做的限制。因此，在原子类型的 API 中，我们可以通过额外的参数指定该原子操作的访存次序(内存序)，默认的内存序是 std::memory_order_seq_cst。</p><p>我们可以把上述 6 中访存次序(内存序)分为 3 类，顺序一致性模型(std::memory_order_seq_cst)，Acquire-Release 模型(std::memory_order_consume, std::memory_order_acquire, std::memory_order_release, std::memory_order_acq_rel,) 和 Relax 模型(std::memory_order_relaxed)。三种不同的内存模型在不同类型的 CPU上(如 X86，ARM，PowerPC等)所带来的代价也不一样。例如，在 X86 或者 X86-64平台下，Acquire-Release 类型的访存序不需要额外的指令来保证原子性，即使顺序一致性类型操作也只需要在写操作(Store)时施加少量的限制，而在读操作(Load)则不需要花费额外的代价来保证原子性。</p><p>处理器一致性(Processor Consistency)模型：处理器一致性(Processor Consistency)模型比顺序一致性模型弱，因此对于某些在顺序一致性模型下能够正确执行的程序在处理器一致性条件下执行时可能会导致错误的结果，处理器一致性模型对访存事件发生次序施加的限制是：(1). 在任意读操作(Load)被允许执行之前，所有在同一处理器中先于这一 Load 的读操作都已完成；(2). 在任意写操作(Store)被允许执行之前，所有在同一处理器中先于这一 Store 的访存操作(包括 Load 和 Store操作)都已完成。上述条件允许 Store 之后的 Load 越过 Store 操作而有限执行。</p><p>弱一致性(Weak Consistency)模型：弱一致性(Weak Consistency)模型的主要思想是将同步操作和普通的访存操作区分开来，程序员必须用硬件可识别的同步操作把对可写共享单元的访存保护起来，以保证多个处理器对可写单元的访问是互斥的。弱一致性对访存事件发生次序的限制如下：(1). 同步操作的执行满足顺序一致性条件; (2). 在任一普通访存操作被允许执行之前，所有在同一处理器中先于这一访存操作的同步操作都已完成; (3). 在任一同步操作被允许执行之前，所有在同一处理器中先于这一同步操作的普通操作都已完成。上述条件允许在同步操作之间的普通访存操作执行时不用考虑进程之间的相关，虽然弱一致性增加了程序员的负担，但是它能有效地提高系统的性能。</p><p>释放一致性(Release Consistency)模型：释放一致性(Release Consistency)模型是对弱一致性(Weak Consistency)模型的改进，它把同步操作进一步分成了获取操作(Acquire)和释放操作(Release)。Acquire 用于获取对某些共享变量的独占访问权，而 Release 则用于释放这种访问权，释放一致性(Release Consistency)模型访存事件发生次序的限制如下：(1). 同步操作的执行满足顺序一致性条件; (2). 在任一普通访存操作被允许执行之前，所有在同一处理器中先于这一访存操作的 Acquire 操作都已完成; (3). 在任一 Release 操作被允许执行之前，所有在同一处理器中先于这一 Release 操作的普通操作都已完成。</p><p>在硬件实现的释放一致性模型中，对共享单元的访存是及时进行的，并在执行获取操作(Acquire)和释放操作(Release)时对齐。在共享虚拟存储系统或者在由软件维护的数据一致性的共享存储系统中，由于通信和数据交换的开销很大，有必要减少通信和数据交换的次数。为此，人们在释放一致性(Release Consistency)模型的基础上提出了急切更新释放一致性模型(Eager Release Consistency)和懒惰更新释放一致性模型(Lazy Release Consistency)。在急切更新释放一致性模型中，在临界区内的多个存数操作对共享内存的更新不是及时进行的，而是在执行 Release 操作之前(即退出临界区之前)集中进行，把多个存数操作合并在一起统一执行，从而减少了通信次数。而在懒惰更新释放一致性模型中，由一个处理器对某单元的存数操作并不是由此处理器主动传播到所有共享该单元的其他处理器，而是在其他处理器要用到此处理器所写的数据时(即其他处理器执行 Acquire 操作时)再向此处理器索取该单元的最新备份，这样可以进一步减少通信量。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;内存模型&quot;&gt;&lt;a href=&quot;#内存模型&quot; class=&quot;headerlink&quot; title=&quot;内存模型&quot;&gt;&lt;/a&gt;内存模型&lt;/h1&gt;&lt;p&gt;原子类型的大多数 API 都需要程序员提供一个 std::memory_order（可译为内存序，访存顺序） 的枚举类型值作</summary>
      
    
    
    
    
    <category term="c++并发" scheme="https://kettycode.github.io/tags/c-%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>mutex</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/mutex/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/mutex/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:17:29.860Z</updated>
    
    <content type="html"><![CDATA[<h1 id="mutex"><a href="#mutex" class="headerlink" title="mutex"></a>mutex</h1><h2 id="Mutex-系列类"><a href="#Mutex-系列类" class="headerlink" title="Mutex 系列类"></a>Mutex 系列类</h2><p>std::mutex : 最基本的Mutex类<br>std::recursive_mutex : 递归Mutex类<br>std::time_mutex : 定时Mutex类<br>std::recursive_timed_mutex : 定时递归Mutex类</p><h2 id="Lock类"><a href="#Lock类" class="headerlink" title="Lock类"></a>Lock类</h2><p>std::lock_guard : 与Mutex RAII相关，方便线程对互斥量上锁。<br>std::unique_lock : 与Mutex RAII相关，但提供了更好的上锁和解锁控制</p><h2 id="其他类型"><a href="#其他类型" class="headerlink" title="其他类型"></a>其他类型</h2><p>std::once_flag<br>std::adopt_lock_t<br>std::defer_lock_t<br>std::try_to_lock_t</p><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><p>std::try_lock : 尝试同时对多个互斥量上锁<br>std::lock : 可以同时对多个互斥量上锁<br>std::call_once : 如果多个线程需要同时调用某个函数，call_once 可以保证多个线程对该函数只调用一次。</p><h2 id="std-mutex介绍"><a href="#std-mutex介绍" class="headerlink" title="std::mutex介绍"></a>std::mutex介绍</h2><p>std::mutex 是C++11 中最基本的互斥量，std::mutex 对象提供了独占所有权的特性——即不支持递归地对 std::mutex 对象上锁，而 std::recursive_lock 则可以递归地对互斥量对象上锁。</p><p>构造函数，std::mutex不允许拷贝构造，也不允许 move 拷贝，最初产生的 mutex 对象是处于 unlocked 状态的。<br>lock()，调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况：(1). 如果该互斥量当前没有被锁住，则调用线程将该互斥量锁住，直到调用 unlock之前，该线程一直拥有该锁。(2). 如果当前互斥量被其他线程锁住，则当前的调用线程被阻塞住。(3). 如果当前互斥量被当前调用线程锁住，则会产生死锁(deadlock)。<br>unlock()， 解锁，释放对互斥量的所有权。<br>try_lock()，尝试锁住互斥量，如果互斥量被其他线程占有，则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况，(1). 如果当前互斥量没有被其他线程占有，则该线程锁住互斥量，直到该线程调用 unlock 释放互斥量。(2). 如果当前互斥量被其他线程锁住，则当前调用线程返回 false，而并不会被阻塞掉。(3). 如果当前互斥量被当前调用线程锁住，则会产生死锁(deadlock)。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>       <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>         <span class="comment">// std::thread</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span>          <span class="comment">// std::mutex</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">volatile</span> <span class="type">int</span> <span class="title">counter</span><span class="params">(<span class="number">0</span>)</span></span>; <span class="comment">// non-atomic counter</span></span><br><span class="line">std::mutex mtx;           <span class="comment">// locks access to counter</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">attempt_10k_increases</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">10000</span>; ++i) &#123;</span><br><span class="line">        <span class="keyword">if</span> (mtx.<span class="built_in">try_lock</span>()) &#123;   <span class="comment">// only increase if currently not locked:</span></span><br><span class="line">            ++counter;</span><br><span class="line">            mtx.<span class="built_in">unlock</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">(<span class="type">int</span> argc, <span class="type">const</span> <span class="type">char</span>* argv[])</span> </span>&#123;</span><br><span class="line">    std::thread threads[<span class="number">10</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">10</span>; ++i)</span><br><span class="line">        threads[i] = std::<span class="built_in">thread</span>(attempt_10k_increases);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; th : threads) th.<span class="built_in">join</span>();</span><br><span class="line">    std::cout &lt;&lt; counter &lt;&lt; <span class="string">&quot; successful increases of the counter.\n&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-recursive-mutex"><a href="#std-recursive-mutex" class="headerlink" title="std::recursive_mutex"></a>std::recursive_mutex</h2><p>std::recursive_mutex 与 std::mutex 一样，也是一种可以被上锁的对象，但是和 std::mutex 不同的是，std::recursive_mutex 允许同一个线程对互斥量多次上锁（即递归上锁），来获得对互斥量对象的多层所有权，std::recursive_mutex 释放互斥量时需要调用与该锁层次深度相同次数的 unlock()，可理解为 lock() 次数和 unlock() 次数相同，除此之外，std::recursive_mutex 的特性和 std::mutex 大致相同。</p><h2 id="std-time-mutex"><a href="#std-time-mutex" class="headerlink" title="std::time_mutex"></a>std::time_mutex</h2><p>std::time_mutex 比 std::mutex 多了两个成员函数，try_lock_for()，try_lock_until()。</p><p>try_lock_for 函数接受一个时间范围，表示在这一段时间范围之内线程如果没有获得锁则被阻塞住（与 std::mutex 的 try_lock() 不同，try_lock 如果被调用时没有获得锁则直接返回 false），如果在此期间其他线程释放了锁，则该线程可以获得对互斥量的锁，如果超时（即在指定时间内还是没有获得锁），则返回 false。</p><p>try_lock_until 函数则接受一个时间点作为参数，在指定时间点未到来之前线程如果没有获得锁则被阻塞住，如果在此期间其他线程释放了锁，则该线程可以获得对互斥量的锁，如果超时（即在指定时间内还是没有获得锁），则返回 false</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>       <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span>         <span class="comment">// std::chrono::milliseconds</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>         <span class="comment">// std::thread</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span>          <span class="comment">// std::timed_mutex</span></span></span><br><span class="line"></span><br><span class="line">std::timed_mutex mtx;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">fireworks</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="comment">// waiting to get a lock: each thread prints &quot;-&quot; every 200ms:</span></span><br><span class="line">  <span class="keyword">while</span> (!mtx.<span class="built_in">try_lock_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">200</span>))) &#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;-&quot;</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// got a lock! - wait for 1s, then this thread prints &quot;*&quot;</span></span><br><span class="line">  std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">1000</span>));</span><br><span class="line">  std::cout &lt;&lt; <span class="string">&quot;*\n&quot;</span>;</span><br><span class="line">  mtx.<span class="built_in">unlock</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  std::thread threads[<span class="number">10</span>];</span><br><span class="line">  <span class="comment">// spawn 10 threads:</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">10</span>; ++i)</span><br><span class="line">    threads[i] = std::<span class="built_in">thread</span>(fireworks);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; th : threads) th.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-recursive-timed-mutex"><a href="#std-recursive-timed-mutex" class="headerlink" title="std::recursive_timed_mutex"></a>std::recursive_timed_mutex</h2><p>std::recursive_timed_mutex 和 std::recursive_mutex 与 std::mutex 的关系一样，std::recursive_timed_mutex 的特性也可以从 std::timed_mutex 推导出来。</p><h2 id="std-lock-guard"><a href="#std-lock-guard" class="headerlink" title="std::lock_guard"></a>std::lock_guard</h2><p>与Mutex RAII相关，方便线程对互斥量上锁，(初始化时自动上锁，作用域结束后自动解锁)</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>       <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>         <span class="comment">// std::thread</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span>          <span class="comment">// std::mutex, std::lock_guard</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdexcept&gt;</span>      <span class="comment">// std::logic_error</span></span></span><br><span class="line"></span><br><span class="line">std::mutex mtx;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print_even</span> <span class="params">(<span class="type">int</span> x)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (x%<span class="number">2</span>==<span class="number">0</span>) std::cout &lt;&lt; x &lt;&lt; <span class="string">&quot; is even\n&quot;</span>;</span><br><span class="line">    <span class="keyword">else</span> <span class="built_in">throw</span> (std::<span class="built_in">logic_error</span>(<span class="string">&quot;not even&quot;</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print_thread_id</span> <span class="params">(<span class="type">int</span> id)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// using a local lock_guard to lock mtx guarantees unlocking on destruction / exception:</span></span><br><span class="line">        <span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">lck</span> <span class="params">(mtx)</span></span>;</span><br><span class="line">        <span class="built_in">print_even</span>(id);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">catch</span> (std::logic_error&amp;) &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;[exception caught]\n&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::thread threads[<span class="number">10</span>];</span><br><span class="line">    <span class="comment">// spawn 10 threads:</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>; i&lt;<span class="number">10</span>; ++i)</span><br><span class="line">        threads[i] = std::<span class="built_in">thread</span>(print_thread_id,i+<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; th : threads) th.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-unique-lock"><a href="#std-unique-lock" class="headerlink" title="std::unique_lock"></a>std::unique_lock</h2><p>与 Mutex RAII 相关，方便线程对互斥量上锁，但提供了更好的上锁和解锁控制<br>unique_lock比lock_guard能提供更多的功能特性（但需要付出性能的一些代价）</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>       <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>         <span class="comment">// std::thread</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span>          <span class="comment">// std::mutex, std::unique_lock</span></span></span><br><span class="line"></span><br><span class="line">std::mutex mtx;           <span class="comment">// mutex for critical section</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">print_block</span> <span class="params">(<span class="type">int</span> n, <span class="type">char</span> c)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// critical section (exclusive access to std::cout signaled by lifetime of lck):</span></span><br><span class="line">    <span class="function">std::unique_lock&lt;std::mutex&gt; <span class="title">lck</span> <span class="params">(mtx)</span></span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i=<span class="number">0</span>; i&lt;n; ++i) &#123;</span><br><span class="line">        std::cout &lt;&lt; c;</span><br><span class="line">    &#125;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">th1</span> <span class="params">(print_block,<span class="number">50</span>,<span class="string">&#x27;*&#x27;</span>)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">th2</span> <span class="params">(print_block,<span class="number">50</span>,<span class="string">&#x27;$&#x27;</span>)</span></span>;</span><br><span class="line"></span><br><span class="line">    th1.<span class="built_in">join</span>();</span><br><span class="line">    th2.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-call-once"><a href="#std-call-once" class="headerlink" title="std::call_once"></a>std::call_once</h2><p>std::call_once 是 C++ 标准库提供的一个函数，用于保证一个函数只会被调用一次，即使在多线程环境下也能确保线程安全。std::call_once 主要用于执行只需要执行一次的初始化操作</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;mutex&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"> </span><br><span class="line">std::once_flag flag1, flag2;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">simple_do_once</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::<span class="built_in">call_once</span>(flag1, []()&#123; std::cout &lt;&lt; <span class="string">&quot;简单样例：调用一次\n&quot;</span>; &#125;);</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">may_throw_function</span><span class="params">(<span class="type">bool</span> do_throw)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (do_throw)</span><br><span class="line">    &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;抛出：call_once 会重试\n&quot;</span>; <span class="comment">// 这会出现不止一次</span></span><br><span class="line">        <span class="keyword">throw</span> std::<span class="built_in">exception</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;没有抛出，call_once 不会再重试\n&quot;</span>; <span class="comment">// 保证一次</span></span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">do_once</span><span class="params">(<span class="type">bool</span> do_throw)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">try</span></span><br><span class="line">    &#123;</span><br><span class="line">        std::<span class="built_in">call_once</span>(flag2, may_throw_function, do_throw);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">catch</span> (...) &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::thread <span class="title">st1</span><span class="params">(simple_do_once)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">st2</span><span class="params">(simple_do_once)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">st3</span><span class="params">(simple_do_once)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">st4</span><span class="params">(simple_do_once)</span></span>;</span><br><span class="line">    st1.<span class="built_in">join</span>();</span><br><span class="line">    st2.<span class="built_in">join</span>();</span><br><span class="line">    st3.<span class="built_in">join</span>();</span><br><span class="line">    st4.<span class="built_in">join</span>();</span><br><span class="line"> </span><br><span class="line">    <span class="function">std::thread <span class="title">t1</span><span class="params">(do_once, <span class="literal">true</span>)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(do_once, <span class="literal">true</span>)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(do_once, <span class="literal">false</span>)</span></span>;</span><br><span class="line">    <span class="function">std::thread <span class="title">t4</span><span class="params">(do_once, <span class="literal">true</span>)</span></span>;</span><br><span class="line">    t1.<span class="built_in">join</span>();</span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t3.<span class="built_in">join</span>();</span><br><span class="line">    t4.<span class="built_in">join</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;mutex&quot;&gt;&lt;a href=&quot;#mutex&quot; class=&quot;headerlink&quot; title=&quot;mutex&quot;&gt;&lt;/a&gt;mutex&lt;/h1&gt;&lt;h2 id=&quot;Mutex-系列类&quot;&gt;&lt;a href=&quot;#Mutex-系列类&quot; class=&quot;headerlink&quot; ti</summary>
      
    
    
    
    
    <category term="c++并发" scheme="https://kettycode.github.io/tags/c-%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>thread</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/thread/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/thread/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:17:38.187Z</updated>
    
    <content type="html"><![CDATA[<h1 id="thread"><a href="#thread" class="headerlink" title="thread"></a>thread</h1><h2 id="std-thread构造"><a href="#std-thread构造" class="headerlink" title="std::thread构造"></a>std::thread构造</h2><p>(1). 默认构造函数，创建一个空的 thread 执行对象。<br>(2). 初始化构造函数，创建一个 thread对象，该 thread对象可被 joinable，新产生的线程会调用 fn 函数，该函数的参数由 args 给出。<br>(3). 拷贝构造函数(被禁用)，意味着 thread 不可被拷贝构造。<br>(4). move 构造函数，move 构造函数，调用成功之后 x 不代表任何 thread 执行对象。<br>注意：可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;utility&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;atomic&gt;</span></span></span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">f1</span><span class="params">(<span class="type">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; ++i) &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Thread &quot;</span> &lt;&lt; n &lt;&lt; <span class="string">&quot; executing\n&quot;</span>;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">10</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">f2</span><span class="params">(<span class="type">int</span>&amp; n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; ++i) &#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Thread 2 executing\n&quot;</span>;</span><br><span class="line">        ++n;</span><br><span class="line">        std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">milliseconds</span>(<span class="number">10</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> n = <span class="number">0</span>;</span><br><span class="line">    std::thread t1; <span class="comment">// t1 is not a thread</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t2</span><span class="params">(f1, n + <span class="number">1</span>)</span></span>; <span class="comment">// pass by value</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t3</span><span class="params">(f2, std::ref(n))</span></span>; <span class="comment">// pass by reference</span></span><br><span class="line">    <span class="function">std::thread <span class="title">t4</span><span class="params">(std::move(t3))</span></span>; <span class="comment">// t4 is now running f2(). t3 is no longer a thread</span></span><br><span class="line">    t2.<span class="built_in">join</span>();</span><br><span class="line">    t4.<span class="built_in">join</span>();</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Final value of n is &quot;</span> &lt;&lt; n &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="std-thread赋值操作："><a href="#std-thread赋值操作：" class="headerlink" title="std::thread赋值操作："></a>std::thread赋值操作：</h2><p>(1). move 赋值操作，如果当前对象不可 joinable，需要传递一个右值引用(rhs)给 move 赋值操作；如果当前对象可被 joinable，则 terminate() 报错。<br>(2). 拷贝赋值操作被禁用，thread 对象不可被拷贝</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span>    <span class="comment">// std::chrono::seconds</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>  <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>    <span class="comment">// std::thread, std::this_thread::sleep_for</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">thread_task</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">    std::this_thread::<span class="built_in">sleep_for</span>(std::chrono::<span class="built_in">seconds</span>(n));</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;hello thread &quot;</span></span><br><span class="line">        &lt;&lt; std::this_thread::<span class="built_in">get_id</span>()</span><br><span class="line">        &lt;&lt; <span class="string">&quot; paused &quot;</span> &lt;&lt; n &lt;&lt; <span class="string">&quot; seconds&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * ===  FUNCTION  =========================================================</span></span><br><span class="line"><span class="comment"> *         Name:  main</span></span><br><span class="line"><span class="comment"> *  Description:  program entry routine.</span></span><br><span class="line"><span class="comment"> * ========================================================================</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">const</span> <span class="type">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::thread threads[<span class="number">5</span>];</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Spawning 5 threads...\n&quot;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">        threads[i] = std::<span class="built_in">thread</span>(thread_task, i + <span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;Done spawning threads! Now wait for them to join\n&quot;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span>&amp; t: threads) &#123;</span><br><span class="line">        t.<span class="built_in">join</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;All threads joined.\n&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> EXIT_SUCCESS;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="其他成员函数"><a href="#其他成员函数" class="headerlink" title="其他成员函数"></a>其他成员函数</h2><ol><li><p>get_id()</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">atd::thread::id main_thread_id = std::this_thread::<span class="built_in">get_id</span>();</span><br></pre></td></tr></table></figure></li><li><p>joinable()</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">std::thread foo;</span><br><span class="line"><span class="function">std::thread <span class="title">bar</span><span class="params">(mythread)</span></span>;</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;Joinable after construction:\n&quot;</span> &lt;&lt; std::boolalpha;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;foo: &quot;</span> &lt;&lt; foo.<span class="built_in">joinable</span>() &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;<span class="comment">//false</span></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;bar: &quot;</span> &lt;&lt; bar.<span class="built_in">joinable</span>() &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;<span class="comment">//true</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (foo.<span class="built_in">joinable</span>()) foo.<span class="built_in">join</span>();</span><br><span class="line"><span class="keyword">if</span> (bar.<span class="built_in">joinable</span>()) bar.<span class="built_in">join</span>();</span><br><span class="line"></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;Joinable after joining:\n&quot;</span> &lt;&lt; std::boolalpha;</span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;foo: &quot;</span> &lt;&lt; foo.<span class="built_in">joinable</span>() &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;<span class="comment">//false</span></span><br><span class="line">std::cout &lt;&lt; <span class="string">&quot;bar: &quot;</span> &lt;&lt; bar.<span class="built_in">joinable</span>() &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;<span class="comment">//false</span></span><br></pre></td></tr></table></figure></li><li><p>join() &#x2F;&#x2F;等待线程完成其执行</p></li><li><p>detach() &#x2F;&#x2F;允许线程独立执行</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>       <span class="comment">// std::cout</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;thread&gt;</span>         <span class="comment">// std::thread, std::this_thread::sleep_for</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;chrono&gt;</span>         <span class="comment">// std::chrono::seconds</span></span></span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pause_thread</span><span class="params">(<span class="type">int</span> n)</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  std::this_thread::<span class="built_in">sleep_for</span> (std::chrono::<span class="built_in">seconds</span>(n));</span><br><span class="line">  std::cout &lt;&lt; <span class="string">&quot;pause of &quot;</span> &lt;&lt; n &lt;&lt; <span class="string">&quot; seconds ended\n&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  std::cout &lt;&lt; <span class="string">&quot;Spawning and detaching 3 threads...\n&quot;</span>;</span><br><span class="line">  std::<span class="built_in">thread</span> (pause_thread,<span class="number">1</span>).<span class="built_in">detach</span>();</span><br><span class="line">  std::<span class="built_in">thread</span> (pause_thread,<span class="number">2</span>).<span class="built_in">detach</span>();</span><br><span class="line">  std::<span class="built_in">thread</span> (pause_thread,<span class="number">3</span>).<span class="built_in">detach</span>();</span><br><span class="line">  std::cout &lt;&lt; <span class="string">&quot;Done spawning threads.\n&quot;</span>;</span><br><span class="line"></span><br><span class="line">  std::cout &lt;&lt; <span class="string">&quot;(the main thread will now pause for 5 seconds)\n&quot;</span>;</span><br><span class="line">  <span class="comment">// give the detached threads time to finish (but not guaranteed!):</span></span><br><span class="line">  <span class="built_in">pause_thread</span>(<span class="number">5</span>);</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>swap() &#x2F;&#x2F;Swaps the state of the object with that of x.</p></li><li><p>native_handle()<br>std::thread::native_handle_type nht &#x3D; std::thread::native_handle();</p></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;thread&quot;&gt;&lt;a href=&quot;#thread&quot; class=&quot;headerlink&quot; title=&quot;thread&quot;&gt;&lt;/a&gt;thread&lt;/h1&gt;&lt;h2 id=&quot;std-thread构造&quot;&gt;&lt;a href=&quot;#std-thread构造&quot; class=&quot;head</summary>
      
    
    
    
    
    <category term="c++并发" scheme="https://kettycode.github.io/tags/c-%E5%B9%B6%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>第二部分</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/template2/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/template2/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:18:09.100Z</updated>
    
    <content type="html"><![CDATA[<h1 id="2-Template的基本语法"><a href="#2-Template的基本语法" class="headerlink" title="2. Template的基本语法"></a>2. Template的基本语法</h1><h2 id="2-1-什么是模板-Template"><a href="#2-1-什么是模板-Template" class="headerlink" title="2.1. 什么是模板(Template)"></a>2.1. 什么是模板(Template)</h2><h2 id="2-2-类模板-Class-Template-的基本语法"><a href="#2-2-类模板-Class-Template-的基本语法" class="headerlink" title="2.2. 类模板 (Class Template) 的基本语法"></a>2.2. 类模板 (Class Template) 的基本语法</h2><h3 id="2-2-1-“模板类”还是“类模板”"><a href="#2-2-1-“模板类”还是“类模板”" class="headerlink" title="2.2.1. “模板类”还是“类模板”"></a>2.2.1. “模板类”还是“类模板”</h3><h3 id="2-2-2-Class-Template的与成员变量定义"><a href="#2-2-2-Class-Template的与成员变量定义" class="headerlink" title="2.2.2. Class Template的与成员变量定义"></a>2.2.2. Class Template的与成员变量定义</h3><p>我们来回顾一下最基本的Class Template声明和定义形式：</p><p>Class Template声明：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">ClassA</span>;</span><br></pre></td></tr></table></figure><p>Class Template定义：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">ClassA</span></span><br><span class="line">&#123;</span><br><span class="line">    T member;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><code>template</code> 是C++关键字，意味着我们接下来将定义一个模板。和函数一样，模板也有一系列参数。这些参数都被囊括在template之后的<code>&lt; &gt;</code>中。在上文的例子中， <code>typename T</code>便是模板参数。回顾一下与之相似的函数参数的声明形式：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(<span class="type">int</span> a)</span></span>;</span><br></pre></td></tr></table></figure><p><code>T</code>则可以类比为函数形参<code>a</code>，这里的“模板形参”<code>T</code>，也同函数形参一样取成任何你想要的名字；<code>typename</code>则类似于例子中函数参数类型<code>int</code>，它表示模板参数中的<code>T</code>将匹配一个类型。除了 <code>typename</code> 之外，我们在后面还要讲到，整型也可以作为模板的参数。</p><p>在定义完模板参数之后，便可以定义你所需要的类。不过在定义类的时候，除了一般类可以使用的类型外，你还可以使用在模板参数中使用的类型 <code>T</code>。可以说，这个 <code>T</code>是模板的精髓，因为你可以通过指定模板实参，将T替换成你所需要的类型。</p><p>例如我们用<code>ClassA&lt;int&gt;</code>来实例化类模板ClassA，那么<code>ClassA&lt;int&gt;</code>可以等同于以下的定义：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注意：这并不是有效的C++语法，只是为了说明模板的作用</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">class</span> &#123;</span><br><span class="line">    <span class="type">int</span> member;</span><br><span class="line">&#125; ClassA&lt;<span class="type">int</span>&gt;;</span><br></pre></td></tr></table></figure><p>可以看出，通过模板参数替换类型，可以获得很多形式相同的新类型，有效减少了代码量。这种用法，我们称之为“泛型”（Generic Programming），它最常见的应用，即是STL中的容器类模板。</p><h3 id="2-2-3-模板的使用"><a href="#2-2-3-模板的使用" class="headerlink" title="2.2.3. 模板的使用"></a>2.2.3. 模板的使用</h3><p>对于C++来说，类型最重要的作用之一就是用它去产生一个变量。例如我们定义了一个动态数组（列表）的类模板<code>vector</code>，它对于任意的元素类型都具有push_back和clear的操作，我们便可以如下定义这个类：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">vector</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">push_back</span><span class="params">(T <span class="type">const</span>&amp;)</span></span>;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">clear</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    T* elements;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>此时我们的程序需要一个整型和一个浮点型的列表，那么便可以通过以下代码获得两个变量：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">vector&lt;<span class="type">int</span>&gt; intArray;</span><br><span class="line">vector&lt;<span class="type">float</span>&gt; floatArray;</span><br></pre></td></tr></table></figure><p>此时我们就可以执行以下的操作，获得我们想要的结果：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">intArray.<span class="built_in">push_back</span>(<span class="number">5</span>);</span><br><span class="line">floatArray.<span class="built_in">push_back</span>(<span class="number">3.0f</span>);</span><br></pre></td></tr></table></figure><p>变量定义的过程可以分成两步来看：第一步，<code>vector&lt;int&gt;</code>将<code>int</code>绑定到类模板<code>vector</code>上，获得了一个“普通的类<code>vector&lt;int&gt;</code>”；第二步通过“vector<int>”定义了一个变量。<br>与“普通的类”不同，类模板是不能直接用来定义变量的 —— 毕竟它的名字是“模板”而不是“类”。例如：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vector unknownVector; <span class="comment">// 错误示例</span></span><br></pre></td></tr></table></figure><p>这样就是错误的。我们把通过类型绑定将类模板变成“普通的类”的过程，称之为模板实例化（Template Instantiate）。实例化的语法是：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">模板名 &lt; [模板实参1，模板实参2，...] &gt;</span><br></pre></td></tr></table></figure><p>看几个例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">vector&lt;<span class="type">int</span>&gt;</span><br><span class="line">ClassA&lt;<span class="type">double</span>&gt;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T0, <span class="keyword">typename</span> T1&gt; <span class="keyword">class</span> <span class="title class_">ClassB</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// Class body ...</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">ClassB&lt;<span class="type">int</span>, <span class="type">float</span>&gt;</span><br></pre></td></tr></table></figure><p>当然，在实例化过程中，被绑定到模板参数上的类型（即模板实参）需要与模板形参正确匹配。<br>就如同函数一样，如果没有提供足够并匹配的参数，模板便不能正确的实例化。</p><h3 id="2-2-4-类模板的成员函数定义"><a href="#2-2-4-类模板的成员函数定义" class="headerlink" title="2.2.4. 类模板的成员函数定义"></a>2.2.4. 类模板的成员函数定义</h3><p>由于C++11正式废弃“模板导出”这一特性，因此在类模板的变量在调用成员函数的时候，需要看到完整的成员函数定义。因此现在的类模板中的成员函数，通常都是以内联的方式实现。<br>例如：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">vector</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">clear</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// Function body</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    T* elements;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>当然，我们也可以将<code>vector&lt;T&gt;::clear</code>的定义部分放在类型之外，只不过这个时候的语法就显得蹩脚许多：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">vector</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">clear</span><span class="params">()</span></span>;  <span class="comment">// 注意这里只有声明</span></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    T* elements;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="type">void</span> vector&lt;T&gt;::<span class="built_in">clear</span>()  <span class="comment">// 函数的实现放在这里</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// Function body</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>函数的实现部分看起来略微拗口。我第一次学到的时候，觉得</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">vector::clear</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// Function body</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样不就行了吗？但是简单想就会知道，<code>clear</code>里面是找不到泛型类型<code>T</code>的符号的。</p><p>因此，在成员函数实现的时候，必须要提供模板参数。此外，为什么类型名不是<code>vector</code>而是<code>vector&lt;T&gt;</code>呢？<br>如果你了解过模板的偏特化与特化的语法，应该能看出，这里的vector<T>在语法上类似于特化&#x2F;偏特化。实际上，这里的函数定义也确实是成员函数的偏特化。特化和偏特化的概念，本文会在第二部分详细介绍。</p><p>综上，正确的成员函数实现如下所示：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="comment">// 模板参数</span></span><br><span class="line"><span class="type">void</span> vector&lt;T&gt; <span class="comment">/*看起来像偏特化*/</span> ::<span class="built_in">clear</span>() <span class="comment">// 函数的实现放在这里</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// Function body</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="2-3-函数模板-Function-Tempalte-入门"><a href="#2-3-函数模板-Function-Tempalte-入门" class="headerlink" title="2.3. 函数模板 (Function Tempalte) 入门"></a>2.3. 函数模板 (Function Tempalte) 入门</h2><h3 id="2-3-1-函数模板的声明和定义"><a href="#2-3-1-函数模板的声明和定义" class="headerlink" title="2.3.1. 函数模板的声明和定义"></a>2.3.1. 函数模板的声明和定义</h3><p>函数模板的语法与类模板基本相同，也是以关键字<code>template</code>和模板参数列表作为声明与定义的开始。模板参数列表中的类型，可以出现在参数、返回值以及函数体中。比方说下面几个例子</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(T <span class="type">const</span>&amp; v)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="function">T <span class="title">foo</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="keyword">typename</span> U&gt; <span class="function">U <span class="title">foo</span><span class="params">(T <span class="type">const</span>&amp;)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    T var;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>无论是函数模板还是类模板，在实际代码中看起来都是“千变万化”的。这些“变化”，主要是因为类型被当做了参数，导致代码中可以变化的部分更多了。</p><p>归根结底，模板无外乎两点：</p><ol><li><p>函数或者类里面，有一些类型我们希望它能变化一下，我们用标识符来代替它，这就是“模板参数”；</p></li><li><p>在需要这些类型的地方，写上相对应的标识符（“模板参数”）。</p></li></ol><p>当然，这里的“可变”实际上在代码编译好后就固定下来了，可以称之为编译期的可变性。</p><p>这里多啰嗦一点，主要也是想告诉大家，模板其实是个很简单的东西。</p><p>下面这个例子，或许可以帮助大家解决以下两个问题：</p><ol><li><p>什么样的需求会使用模板来解决？</p></li><li><p>怎样把脑海中的“泛型”变成真正“泛型”的代码？</p></li></ol><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">举个例子：generic typed function ‘add’</span><br></pre></td></tr></table></figure><p>在我遇到的朋友中，即便如此对他解释了模板，即便他了解了模板，也仍然会对模板产生畏难情绪。毕竟从形式上来说，模板化的类和模板化的函数都要较非模板的版本更加复杂，阅读代码所需要理解的内容也有所增多。</p><p>如何才能克服这一问题，最终视模板如平坦代码呢？</p><p>答案只有一个：<strong>无他，唯手熟尔</strong>。</p><p>在学习模板的时候，要反复做以下的思考和练习：</p><ol><li><p>提出问题：我的需求能不能用模板来解决？</p></li><li><p>怎么解决？</p></li><li><p>把解决方案用代码写出来。</p></li><li><p>如果失败了，找到原因。是知识有盲点（例如不知道怎么将 <code>T&amp;</code> 转化成 <code>T</code>），还是不可行（比如试图利用浮点常量特化类模板，但实际上这样做是不可行的）？</p></li></ol><p>通过重复以上的练习，应该可以对模板的语法和含义都有所掌握。如果提出问题本身有困难，或许下面这个经典案例可以作为你思考的开始：</p><ol><li><p>写一个泛型的数据结构：例如，线性表，数组，链表，二叉树；</p></li><li><p>写一个可以在不同数据结构、不同的元素类型上工作的泛型函数，例如求和；</p></li></ol><p>当然和“设计模式”一样，模板在实际应用中，也会有一些固定的需求和解决方案。比较常见的场景包括：泛型（最基本的用法）、通过类型获得相应的信息（型别萃取）、编译期间的计算、类型间的推导和变换（从一个类型变换成另外一个类型，比如boost::function）。这些本文在以后的章节中会陆续介绍。</p><h3 id="2-3-2-函数模板的使用"><a href="#2-3-2-函数模板的使用" class="headerlink" title="2.3.2. 函数模板的使用"></a>2.3.2. 函数模板的使用</h3><p>我们先来看一个简单的函数模板，两个数相加：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="function">T <span class="title">Add</span><span class="params">(T a, T b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>函数模板的调用格式是：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">函数模板名 &lt; 模板参数列表 &gt; ( 参数 )</span><br></pre></td></tr></table></figure><p>例如，我们想对两个 <code>int</code> 求和，那么套用类的模板实例化方法，我们可以这么写：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">5</span>;</span><br><span class="line"><span class="type">int</span> b = <span class="number">3</span>;</span><br><span class="line"><span class="type">int</span> result = <span class="built_in">Add</span>&lt;<span class="type">int</span>&gt;(a, b);</span><br></pre></td></tr></table></figure><p>这时我们等于拥有了一个新函数：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="built_in">Add</span>&lt;<span class="type">int</span>&gt;(<span class="type">int</span> a, <span class="type">int</span> b) &#123; <span class="keyword">return</span> a + b; &#125;</span><br></pre></td></tr></table></figure><p>这时在另外一个偏远的程序角落，你也需要求和。而此时你的参数类型是 <code>float</code> ，于是你写下：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Add</span>&lt;<span class="type">float</span>&gt;(a, b);</span><br></pre></td></tr></table></figure><p>一切看起来都很完美。但如果你具备程序员的最佳美德——懒惰——的话，你肯定会这样想，我在调用 <code>Add&lt;int&gt;(a, b)</code> 的时候， <code>a</code> 和 <code>b</code> 匹配的都是那个 <code>T</code>。编译器就应该知道那个 <code>T</code> 实际上是 <code>int</code> 呀？为什么还要我多此一举写 <code>Add&lt;int&gt;</code> 呢？<br>唔，我想说的是，编译器的作者也是这么想的。所以实际上你在编译器里面写下以下片段：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">5</span>;</span><br><span class="line"><span class="type">int</span> b = <span class="number">3</span>;</span><br><span class="line"><span class="type">int</span> result = <span class="built_in">Add</span>(a, b);</span><br></pre></td></tr></table></figure><p>编译器会心领神会地将 <code>Add</code> 变成 <code>Add&lt;int&gt;</code>。但是编译器不能面对模棱两可的答案。比如你这么写的话呢？</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>  a = <span class="number">5</span>;</span><br><span class="line"><span class="type">char</span> b = <span class="number">3</span>;</span><br><span class="line"><span class="type">int</span>  result = <span class="built_in">Add</span>(a, b);</span><br></pre></td></tr></table></figure><p>第一个参数 <code>a</code> 告诉编译器，这个 <code>T</code> 是 <code>int</code>。编译器点点头说，好。但是第二个参数 <code>b</code> 不高兴了，告诉编译器说，你这个 <code>T</code>，其实是 <code>char</code>。<br>两个参数各自指导 <code>T</code> 的类型，编译器就不知道怎么做了。在Visual Studio 2012下，会有这样的提示：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">error C2782: &#x27;T _1_2_2::Add(T,T)&#x27; : template parameter &#x27;T&#x27; is ambiguous</span><br></pre></td></tr></table></figure><p>好吧，”ambiguous”，这个提示再明确不过了。</p><p>不过，只要你别逼得编译器精神分裂的话，编译器其实是非常聪明的，它可以从很多的蛛丝马迹中，猜测到你真正的意图，有如下面的例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">A</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="function">T <span class="title">foo</span><span class="params">( A&lt;T&gt; v )</span></span>;</span><br><span class="line"></span><br><span class="line">A&lt;<span class="type">int</span>&gt; v;</span><br><span class="line"><span class="built_in">foo</span>(v);<span class="comment">// 它能准确地猜到 T 是 int.</span></span><br></pre></td></tr></table></figure><p>咦，编译器居然绕过了A这个外套，猜到了 <code>T</code> 匹配的是 <code>int</code>。编译器是怎么完成这一“魔法”的，我们暂且不表，2.2节时再和盘托出。</p><p>下面轮到你的练习时间了。你试着写了很多的例子，但是其中一个你还是犯了疑惑：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">float</span> data[<span class="number">1024</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="function">T <span class="title">GetValue</span><span class="params">(<span class="type">int</span> i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">static_cast</span>&lt;T&gt;(data[i]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">float</span> a = <span class="built_in">GetValue</span>(<span class="number">0</span>);<span class="comment">// 出错了！</span></span><br><span class="line"><span class="type">int</span> b = <span class="built_in">GetValue</span>(<span class="number">1</span>);<span class="comment">// 也出错了！</span></span><br></pre></td></tr></table></figure><p>为什么会出错呢？你仔细想了想，原来编译器是没办法去根据返回值推断类型的。函数调用的时候，返回值被谁接受还不知道呢。如下修改后，就一切正常了：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">float</span> a = <span class="built_in">GetValue</span>&lt;<span class="type">float</span>&gt;(<span class="number">0</span>);</span><br><span class="line"><span class="type">int</span> b = <span class="built_in">GetValue</span>&lt;<span class="type">int</span>&gt;(<span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>嗯，是不是so easy啊？嗯，你又信心满满的做了一个练习：</p><p>你要写一个函数模板叫 <code>c_style_cast</code>，顾名思义，执行的是C风格的转换。然后出于方便起见，你希望它能和 <code>static_cast</code> 这样的内置转换有同样的写法。于是你写了一个use case。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DstT dest = <span class="built_in">c_style_cast</span>&lt;DstT&gt;(src);</span><br></pre></td></tr></table></figure><p>根据调用形式你知道了，有 <code>DstT</code> 和 <code>SrcT</code> 两个模板参数。参数只有一个， <code>src</code>，所以函数的形参当然是这么写了： <code>(SrcT src)</code>。实现也很简单， <code>(DstT)v</code>。</p><p>我们把手上得到的信息来拼一拼，就可以编写自己的函数模板了：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> SrcT, <span class="keyword">typename</span> DstT&gt; <span class="function">DstT <span class="title">c_style_cast</span><span class="params">(SrcT v)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (DstT)(v);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> v = <span class="number">0</span>;</span><br><span class="line"><span class="type">float</span> i = <span class="built_in">c_style_cast</span>&lt;<span class="type">float</span>&gt;(v);</span><br></pre></td></tr></table></figure><p>嗯，很Easy嘛！我们F6一下…咦！这是什么意思！</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">error C2783: <span class="string">&#x27;DstT _1_2_2::c_style_cast(SrcT)&#x27;</span> : could <span class="keyword">not</span> deduce <span class="keyword">template</span> argument <span class="keyword">for</span> <span class="string">&#x27;DstT&#x27;</span></span><br></pre></td></tr></table></figure><p>然后你仔细的比较了一下，然后发现 … 模板参数有两个，而参数里面能得到的只有 <code>SrcT</code> 一个。结合出错信息看来关键在那个 <code>DstT</code> 上。这个时候，你死马当活马医，把模板参数写完整了：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">float</span> i = <span class="built_in">c_style_cast</span>&lt;<span class="type">int</span>, <span class="type">float</span>&gt;(v);</span><br></pre></td></tr></table></figure><p>嗯，很顺利的通过了。难道C++不能支持让参数推导一部分模板参数吗？</p><p>当然是可以的。只不过在部分推导、部分指定的情况下，编译器对模板参数的顺序是有限制的：<strong>先写需要指定的模板参数，再把能推导出来的模板参数放在后面</strong>。</p><p>在这个例子中，能推导出来的是 <code>SrcT</code>，需要指定的是 <code>DstT</code>。把函数模板写成下面这样就可以了：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> DstT, <span class="keyword">typename</span> SrcT&gt; <span class="function">DstT <span class="title">c_style_cast</span><span class="params">(SrcT v)</span><span class="comment">// 模板参数 DstT 需要人肉指定，放前面。</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (DstT)(v);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> v = <span class="number">0</span>;</span><br><span class="line"><span class="type">float</span> i = <span class="built_in">c_style_cast</span>&lt;<span class="type">float</span>&gt;(v);  <span class="comment">// 形象地说，DstT会先把你指定的参数吃掉，剩下的就交给编译器从函数参数列表中推导啦。</span></span><br></pre></td></tr></table></figure><h2 id="2-4-整型也可是Template参数"><a href="#2-4-整型也可是Template参数" class="headerlink" title="2.4. 整型也可是Template参数"></a>2.4. 整型也可是Template参数</h2><p>模板参数除了类型外（包括基本类型、结构、类类型等），也可以是一个整型数（Integral Number）。这里的整型数比较宽泛，包括布尔型，不同位数、有无符号的整型，甚至包括指针。我们将整型的模板参数和类型作为模板参数来做一个对比：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">TemplateWithType</span>;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="type">int</span>      V&gt; <span class="keyword">class</span> <span class="title class_">TemplateWithValue</span>;</span><br></pre></td></tr></table></figure><p>我想这个时候你也更能理解 <code>typename</code> 的意思了：它相当于是模板参数的“类型”，告诉你 <code>T</code> 是一个 <code>typename</code>。</p><p>按照C++ Template最初的想法，模板不就是为了提供一个类型安全、易于调试的宏吗？有类型就够了，为什么要引入整型参数呢？考虑宏，它除了代码替换，还有一个作用是作为常数出现。所以整型模板参数最基本的用途，也是定义一个常数。例如这段代码的作用：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T, <span class="type">int</span> Size&gt; <span class="keyword">struct</span> <span class="title class_">Array</span></span><br><span class="line">&#123;</span><br><span class="line">    T data[Size];</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">Array&lt;<span class="type">int</span>, <span class="number">16</span>&gt; arr;</span><br></pre></td></tr></table></figure><p>便相当于下面这段代码：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">IntArrayWithSize16</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> data[<span class="number">16</span>]; <span class="comment">// int 替换了 T, 16 替换了 Size</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">IntArrayWithSize16 arr;</span><br></pre></td></tr></table></figure><p>其中有一点需要注意，因为模板的匹配是在编译的时候完成的，所以实例化模板的时候所使用的参数，也必须要在编译期就能确定。例如以下的例子编译器就会报错：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="type">int</span> i&gt; <span class="keyword">class</span> <span class="title class_">A</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">int</span> x = <span class="number">3</span>;</span><br><span class="line">    A&lt;<span class="number">5</span>&gt; a; <span class="comment">// 正确！</span></span><br><span class="line">    A&lt;x&gt; b; <span class="comment">// error C2971: &#x27;_1_3::A&#x27; : template parameter &#x27;i&#x27; : &#x27;x&#x27; : a local variable cannot be used as a non-type argument</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>因为x不是一个编译期常量，所以 <code>A&lt;x&gt;</code> 就会告诉你，x是一个局部变量，不能作为一个模板参数出现。</p><p>嗯，这里我们再来写几个相对复杂的例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="type">int</span> i&gt; <span class="keyword">class</span> <span class="title class_">A</span> </span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">(<span class="type">int</span>)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="type">uint8_t</span> a, <span class="keyword">typename</span> b, <span class="type">void</span>* c&gt; <span class="keyword">class</span> <span class="title class_">B</span> &#123;&#125;;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="type">bool</span>, <span class="built_in">void</span> (*a)()&gt; <span class="keyword">class</span> <span class="title class_">C</span> &#123;&#125;;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="built_in">void</span> (A&lt;<span class="number">3</span>&gt;::*a)(<span class="type">int</span>)&gt; <span class="keyword">class</span> <span class="title class_">D</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="type">int</span> i&gt; <span class="function"><span class="type">int</span> <span class="title">Add</span><span class="params">(<span class="type">int</span> a)</span><span class="comment">// 当然也能用于函数模板</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a + i;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    A&lt;<span class="number">5</span>&gt; a;</span><br><span class="line">    B&lt;<span class="number">7</span>, A&lt;<span class="number">5</span>&gt;, <span class="literal">nullptr</span>&gt;b; <span class="comment">// 模板参数可以是一个无符号八位整数，可以是模板生成的类；可以是一个指针。</span></span><br><span class="line">    C&lt;<span class="literal">false</span>, &amp;foo&gt; c;      <span class="comment">// 模板参数可以是一个bool类型的常量，甚至可以是一个函数指针。</span></span><br><span class="line">    D&lt;&amp;A&lt;<span class="number">3</span>&gt;::foo&gt; d;       <span class="comment">// 丧心病狂啊！它还能是一个成员函数指针！</span></span><br><span class="line">    <span class="type">int</span> x = <span class="built_in">Add</span>&lt;<span class="number">3</span>&gt;(<span class="number">5</span>);     <span class="comment">// x == 8。因为整型模板参数无法从函数参数获得，所以只能是手工指定啦。</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="type">float</span> a&gt; <span class="keyword">class</span> <span class="title class_">E</span> &#123;&#125;; <span class="comment">// ERROR: 别闹！早说过只能是整数类型的啦！</span></span><br></pre></td></tr></table></figure><p>当然，除了单纯的用作常数之外，整型参数还有一些其它的用途。这些“其它”用途最重要的一点是让类型也可以像整数一样运算。《Modern C++ Design》给我们展示了很多这方面的例子。不过你不用急着去阅读那本天书，我们会在做好足够的知识铺垫后，让你轻松学会这些招数。</p><h2 id="2-5-模板形式与功能是统一的"><a href="#2-5-模板形式与功能是统一的" class="headerlink" title="2.5. 模板形式与功能是统一的"></a>2.5. 模板形式与功能是统一的</h2><p>第一章走马观花的带着大家复习了一下C++ Template的基本语法形式，也解释了包括 <code>typename</code> 在内，类&#x2F;函数模板写法中各个语法元素的含义。形式是功能的外在体现，介绍它们也是为了让大家能理解到，模板之所以写成这种形式是有必要的，而不是语言的垃圾成分。</p><p>从下一章开始，我们便进入了更加复杂和丰富的世界：讨论模板的匹配规则。其中有令人望而生畏的特化与偏特化。但是，请相信我们在序言中所提到的：将模板作为一门语言来看待，它会变得有趣而简单。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;2-Template的基本语法&quot;&gt;&lt;a href=&quot;#2-Template的基本语法&quot; class=&quot;headerlink&quot; title=&quot;2. Template的基本语法&quot;&gt;&lt;/a&gt;2. Template的基本语法&lt;/h1&gt;&lt;h2 id=&quot;2-1-什么是模板-T</summary>
      
    
    
    
    
    <category term="c++ template" scheme="https://kettycode.github.io/tags/c-template/"/>
    
  </entry>
  
  <entry>
    <title>第一部分-迈向C++</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E7%8E%B0%E4%BB%A3C++/base1/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E7%8E%B0%E4%BB%A3C++/base1/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:20:27.062Z</updated>
    
    <content type="html"><![CDATA[<h1 id="迈向现代C"><a href="#迈向现代C" class="headerlink" title="迈向现代C++"></a>迈向现代C++</h1><h2 id="被弃用的特性"><a href="#被弃用的特性" class="headerlink" title="被弃用的特性"></a>被弃用的特性</h2><p>注意：弃用并非彻底不能用，只是用于暗示程序员这些特性将从未来的标准中消失，应该尽量避免使用。但是，已弃用的特性依然是标准库的一部分，并且出于兼容性的考虑，大部分特性其实会『永久』保留</p><p>1.不再允许字符串字面值常量赋值给一个 char *。如果需要用字符串字面值常量赋值和初始化一个 char *，应该使用 const char * 或者 auto。<br>2.C++98 异常说明、 unexpected_handler、set_unexpected() 等相关特性被弃用，应该使用 noexcept。<br>3.auto_ptr 被弃用，应使用 unique_ptr。<br>4.bool 类型的 ++ 操作被弃用。<br>5.C 语言风格的类型转换被弃用（即在变量前使用 (convert_type)），应该使用 static_cast、reinterpret_cast、const_cast 来进行类型转换。<br>6.特别地，在最新的 C++17 标准中弃用了一些可以使用的 C 标准库，例如 <ccomplex>、<cstdalign>、<cstdbool> 与 <ctgmath> 等<br>7.std::bind,std::function,export等特效</p><h2 id="与C的兼容性"><a href="#与C的兼容性" class="headerlink" title="与C的兼容性"></a>与C的兼容性</h2><p>在编写 C++ 时，也应该尽可能的避免使用诸如 void* 之类的程序风格。而在不得不使用 C 时，应该注意使用 extern “C” 这种特性，将 C 语言的代码与 C++代码进行分离编译，再统一链接这种做法。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;迈向现代C&quot;&gt;&lt;a href=&quot;#迈向现代C&quot; class=&quot;headerlink&quot; title=&quot;迈向现代C++&quot;&gt;&lt;/a&gt;迈向现代C++&lt;/h1&gt;&lt;h2 id=&quot;被弃用的特性&quot;&gt;&lt;a href=&quot;#被弃用的特性&quot; class=&quot;headerlink&quot; titl</summary>
      
    
    
    
    
    <category term="modern c++" scheme="https://kettycode.github.io/tags/modern-c/"/>
    
  </entry>
  
  <entry>
    <title>第五部分</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/template5/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/template5/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:18:35.133Z</updated>
    
    <content type="html"><![CDATA[<h1 id="5-未完成章节"><a href="#5-未完成章节" class="headerlink" title="5. 未完成章节"></a>5. 未完成章节</h1><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"># 6. 元编程下的数据结构与算法</span><br><span class="line">## 6.1. 表达式与数值计算</span><br><span class="line">## 6.2. 获得类型的属性——类型萃取（Type Traits） </span><br><span class="line">## 6.3. 列表与数组</span><br><span class="line">## 6.4. 字典结构</span><br><span class="line">## 6.5. “快速”排序</span><br><span class="line">## 6.6. 其它常用的“轮子”</span><br><span class="line"></span><br><span class="line"># 7. 非模板的编译期计算</span><br><span class="line"></span><br><span class="line"># 8. 模板的进阶技巧</span><br><span class="line">## 8.1. 嵌入类</span><br><span class="line">## 8.2. Template-Template Class</span><br><span class="line">## 8.3. 高阶函数</span><br><span class="line">## 8.4. 闭包：模板的“基于对象”</span><br><span class="line">stl allocator?</span><br><span class="line">mpl::apply</span><br><span class="line">## 8.5. 占位符(placeholder)：在C++中实现方言的基石</span><br><span class="line">## 8.6. 编译期“多态”</span><br><span class="line"></span><br><span class="line">#   9. 模板的威力：从foreach, transform到Linq</span><br><span class="line">## 9.1. Foreach与Transform</span><br><span class="line">## 9.2. Boost中的模板</span><br><span class="line">Any Spirit Hana TypeErasure</span><br><span class="line">## 9.3. Reactor、Linq与C++中的实践</span><br><span class="line">## 9.4. 更高更快更强：从Linq到FP</span><br><span class="line"></span><br><span class="line">#   10. 结语：讨论有益，争端无用</span><br><span class="line">## 10.1. 更好的编译器，更友善的出错信息</span><br><span class="line">## 10.2. 模板的症结：易于实现，难于完美</span><br><span class="line">## 10.3. 一些期望</span><br><span class="line">alexandrescu 关于 min max 的讨论：《再谈Min和Max》</span><br><span class="line">std::experimental::any / boost.any 对于 reference 的处理</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;5-未完成章节&quot;&gt;&lt;a href=&quot;#5-未完成章节&quot; class=&quot;headerlink&quot; title=&quot;5. 未完成章节&quot;&gt;&lt;/a&gt;5. 未完成章节&lt;/h1&gt;&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td </summary>
      
    
    
    
    
    <category term="c++ template" scheme="https://kettycode.github.io/tags/c-template/"/>
    
  </entry>
  
  <entry>
    <title>第三部分</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/template3/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/template3/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:18:18.606Z</updated>
    
    <content type="html"><![CDATA[<h1 id="3-模板元编程基础"><a href="#3-模板元编程基础" class="headerlink" title="3. 模板元编程基础"></a>3. 模板元编程基础</h1><h2 id="3-1-编程，元编程，模板元编程"><a href="#3-1-编程，元编程，模板元编程" class="headerlink" title="3.1. 编程，元编程，模板元编程"></a>3.1. 编程，元编程，模板元编程</h2><p>技术的学习是一个登山的过程。第一章是最为平坦的山脚道路。而从这一章开始，则是正式的爬坡。无论是我写作还是你阅读，都需要付出比第一章更多的代价。那么问题就是，付出更多的精力学习模板是否值得？</p><p>这个问题很功利，但是一针见血。因为技术的根本目的在于解决需求。那C++的模板能做什么？</p><p>一个高（树）大（新）上（风）的回答是，C++里面的模板，犹如C中的宏、C和Java中的自省（restropection）和反射（reflection），是一个改变语言内涵，拓展语言外延的存在。</p><p>程序最根本的目的是什么？复现真实世界或人所构想的规律，减少重复工作的成本，或通过提升规模完成人所不能及之事。但是世间之事万千，有限的程序如何重现复杂的世界呢？</p><p>答案是“抽象”。论及具体手段，无外乎“求同”与“存异”：概括一般规律，处理特殊情况。这也是软件工程所追求的目标。一般规律概括的越好，我们所付出的劳动也就越少。</p><p>同样的，作为脑力劳动的产品，程序本身也是有规律性的。《Modern C++ Design》中的前言就抛出了一连串有代表性的问题：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">如何撰写更高级的C++程式？</span><br><span class="line">如何应付即使在很干净的设计中仍然像雪崩一样的不相干细节？</span><br><span class="line">如何构建可复用组件，使得每次在不同程式中应用组件时无需大动干戈？</span><br></pre></td></tr></table></figure><p>我们以数据结构举例。在程序里，你需要一些堆栈。这个堆栈的元素可能是整数、浮点或者别的什么类型。一份整型堆栈的代码可能是：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">StackInt</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">push</span><span class="params">(<span class="type">int</span> v)</span></span>;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">pop</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">Find</span><span class="params">(<span class="type">int</span> x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; size; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(data[i] == x) &#123; <span class="keyword">return</span> i; &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ... 其他代码 ...</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>如果你要支持浮点了，那么你只能将代码再次拷贝出来，并作如下修改：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">StackFloat</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">push</span><span class="params">(<span class="type">float</span> v)</span></span>;</span><br><span class="line">    <span class="function"><span class="type">float</span> <span class="title">pop</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">Find</span><span class="params">(<span class="type">float</span> x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; size; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(data[i] == x) &#123; <span class="keyword">return</span> i; &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ... 其他代码 ...</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>当然也许你觉得这样做能充分体会代码行数增长的成就感。但是有一天，你突然发现：呀，<code>Find</code> 函数实现有问题了。怎么办？这个时候也许你只有两份这样的代码，那好说，一一去修正就好了。如果你有十个呢？二十个？五十个？</p><p>时间一长，你就厌倦了这样的生活。你觉得每个堆栈都差不多，但是又有点不一样。为了这一点点不一样，你付出了太多的时间。吃饭的时间，泡妞的时间，睡觉的时间，看岛国小电影顺便练习小臂力量的时间。</p><p>于是便诞生了新的技术，来消解我们的烦恼。</p><p>这个技术的名字，并不叫“模板”，而是叫“元编程”。</p><p>元（meta）无论在中文还是英文里，都是个很“抽象（abstract）”的词。因为它的本意就是“抽象”。元编程，也可以说就是“编程的抽象”。用更好理解的说法，元编程意味着你撰写一段程序A，程序A会运行后生成另外一个程序B，程序B才是真正实现功能的程序。那么这个时候程序A可以称作程序B的元程序，撰写程序A的过程，就称之为“元编程”。</p><p>回到我们的堆栈的例子。真正执行功能的，其实仍然是浮点的堆栈、整数的堆栈、各种你所需要的类型的堆栈。但是因为这些堆栈之间太相似了，仅仅有着些微的不同，我们为什么不能有一个将相似之处囊括起来，同时又能分别体现出不同之处的程序呢？很多语言都提供了这样的机会。C中的宏，C++中的模板，Python中的Duck Typing，广义上将都能够实现我们的思路。</p><p>我们的目的，是找出程序之间的相似性，进行“元编程”。而在C++中，元编程的手段，可以是宏，也可以是模板。</p><p>宏的例子姑且不论，我们来看一看模板：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Stack</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">push</span><span class="params">(T v)</span></span>;</span><br><span class="line">    <span class="function">T <span class="title">pop</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">Find</span><span class="params">(T x)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; size; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(data[i] == x) &#123; <span class="keyword">return</span> i; &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// ... 其他代码 ...</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> Stack&lt;<span class="type">int</span>&gt;   StackInt;</span><br><span class="line"><span class="keyword">typedef</span> Stack&lt;<span class="type">float</span>&gt; StackFloat;</span><br></pre></td></tr></table></figure><p>通过模板，我们可以将形形色色的堆栈代码分为两个部分，一个部分是不变的接口，以及近乎相同的实现；另外一部分是元素的类型，它们是需要变化的。因此同函数类似，需要变化的部分，由模板参数来反映；不变的部分，则是模板内的代码。可以看到，使用模板的代码，要比不使用模板的代码简洁许多。</p><p>如果元编程中所有变化的量（或者说元编程的参数），都是类型，那么这样的编程，我们有个特定的称呼，叫“泛型”。</p><p>但是你会问，模板的发明，仅仅是为了做和宏几乎一样的替换工作吗？可以说是，也可以说不是。一方面，很多时候模板就是为了替换类型，这个时候作用上其实和宏没什么区别。只是宏是基于文本的替换，被替换的文本本身没有任何语义。只有替换完成，编译器才能进行接下来的处理。而模板会在分析模板时以及实例化模板时时候都会进行检查，而且源代码中也能与调试符号一一对应，所以无论是编译时还是运行时，排错都相对简单。</p><p>但是模板和宏也有很大的不同，否则此文也就不能成立了。模板最大的不同在于它是“可以运算”的。我们来举一个例子，不过可能有点牵强。考虑我们要写一个向量逐分量乘法。只不过这个向量，它非常的大。所以为了保证速度，我们需要使用SIMD指令进行加速。假设我们有以下指令可以使用：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Int8,16: N/A</span><br><span class="line">Int32  : VInt32Mul(int32x4, int32x4)</span><br><span class="line">Int64  : VInt64Mul(int64x4, int64x4)</span><br><span class="line">Float  : VInt64Mul(floatx2, floatx2)</span><br></pre></td></tr></table></figure><p>所以对于Int8和Int16，我们需要提升到Int32，而Int32和Int64，各自使用自己的指令。所以我们需要实现下的逻辑：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(v4a, v4b : vectorsA, vectorsB)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> type is Int8, <span class="function">Int16</span></span><br><span class="line"><span class="function">        <span class="title">VInt32Mul</span><span class="params">( ConvertToInt32(v4a), ConvertToInt32(v4b) )</span></span></span><br><span class="line"><span class="function">    elif type is Int32</span></span><br><span class="line"><span class="function">        <span class="title">VInt32Mul</span><span class="params">( v4a, v4b )</span></span></span><br><span class="line"><span class="function">    elif type is Float</span></span><br><span class="line"><span class="function">        ...</span></span><br><span class="line"><span class="function">&#125;</span></span><br></pre></td></tr></table></figure><p>这里的问题就在于，如何根据 <code>type</code> 分别提供我们需要的实现？这里有两个难点。首先， <code>if(type == xxx) &#123;&#125;</code> 是不存在于C++中的。第二，即便存在根据 <code>type</code> 的分配方法，我们也不希望它在运行时branch，这样会变得很慢。我们希望它能按照类型直接就把代码编译好，就跟直接写的一样。</p><p>嗯，聪明你果然想到了，重载也可以解决这个问题。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">GenericMul</span>(int8x4,  int8x4);</span><br><span class="line"><span class="built_in">GenericMul</span>(int16x4, int16x4);</span><br><span class="line"><span class="built_in">GenericMul</span>(int32x4, int32x4);</span><br><span class="line"><span class="built_in">GenericMul</span>(int64x4, int64x4);</span><br><span class="line"><span class="comment">// 其它 Generic Mul ...</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(v4a, v4b : vectorsA, vectorsB)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">GenericMul</span>(v4a, v4b);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这样不就可以了吗？</p><p>唔，你赢了，是这样没错。但是问题是，我这个平台是你可没见过，它叫 <code>Deep Thought</code>， 特别缺心眼儿，不光有 <code>int8</code>，还有更奇怪的 <code>int9</code>, <code>int11</code>，以及可以代表世间万物的 <code>int42</code>。你总不能为之提供所有的重载吧？这简直就像你枚举了所有程序的输入，并为之提供了对应的输出一样。</p><p>好吧，我承认这个例子还是太牵强了。不过相信我，在你阅读完第二章和第三章之后，你会将这些特性自如地运用到你的程序之中。你的程序将会变成体现模板“可运算”威力的最好例子。</p><h2 id="3-2-模板世界的If-Then-Else：类模板的特化与偏特化"><a href="#3-2-模板世界的If-Then-Else：类模板的特化与偏特化" class="headerlink" title="3.2. 模板世界的If-Then-Else：类模板的特化与偏特化"></a>3.2. 模板世界的If-Then-Else：类模板的特化与偏特化</h2><h3 id="3-2-1-根据类型执行代码"><a href="#3-2-1-根据类型执行代码" class="headerlink" title="3.2.1. 根据类型执行代码"></a>3.2.1. 根据类型执行代码</h3><p>前一节的示例提出了一个要求：需要做出根据类型执行不同代码。要达成这一目的，模板并不是唯一的途径。比如之前我们所说的重载。如果把眼界放宽一些，虚函数也是根据类型执行代码的例子。此外，在C语言时代，也会有一些技法来达到这个目的，比如下面这个例子，我们需要对两个浮点做加法， 或者对两个整数做乘法：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Variant</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="class"><span class="keyword">union</span></span></span><br><span class="line"><span class="class">    &#123;</span></span><br><span class="line">        <span class="type">int</span> x;</span><br><span class="line">        <span class="type">float</span> y;</span><br><span class="line">    &#125; data;</span><br><span class="line">    uint32 typeId;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">Variant <span class="title function_">addFloatOrMulInt</span><span class="params">(Variant <span class="type">const</span>* a, Variant <span class="type">const</span>* b)</span></span><br><span class="line">&#123;</span><br><span class="line">    Variant ret;</span><br><span class="line">    assert(a-&gt;typeId == b-&gt;typeId);</span><br><span class="line">    <span class="keyword">if</span> (a-&gt;typeId == TYPE_INT)</span><br><span class="line">    &#123;</span><br><span class="line">        ret.x = a-&gt;x * b-&gt;x;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        ret.y = a-&gt;y + b-&gt;y;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>更常见的是 <code>void*</code>:</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">define <span class="title">BIN_OP</span><span class="params">(type, a, op, b, result)</span> <span class="params">(*(type *)(result))</span> </span>= (*(type <span class="type">const</span> *)(a)) <span class="built_in">op</span> (*(type <span class="type">const</span>*)(b))</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">doDiv</span><span class="params">(<span class="type">void</span>* out, <span class="type">void</span> <span class="type">const</span>* data0, <span class="type">void</span> <span class="type">const</span>* data1, DATA_TYPE type)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(type == TYPE_INT)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">BIN_OP</span>(<span class="type">int</span>, data0, *, data1, out);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">BIN_OP</span>(<span class="type">float</span>, data0, +, data1, out);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在C++中比如在 <code>Boost.Any</code> 的实现中，运用了 <code>typeid</code> 来查询类型信息。和 <code>typeid</code> 同属于RTTI机制的 <code>dynamic_cast</code>，也经常会用来做类型判别的工作。我想你应该写过类似于下面的代码：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">IAnimal* animal = <span class="built_in">GetAnimalFromSystem</span>();</span><br><span class="line"></span><br><span class="line">IDog* maybeDog = <span class="built_in">dynamic_cast</span>&lt;IDog*&gt;(animal);</span><br><span class="line"><span class="keyword">if</span>(maybeDog)</span><br><span class="line">&#123;</span><br><span class="line">    maybeDog-&gt;<span class="built_in">Wangwang</span>();</span><br><span class="line">&#125;</span><br><span class="line">ICat* maybeCat = <span class="built_in">dynamic_cast</span>&lt;ICat*&gt;(animal);</span><br><span class="line"><span class="keyword">if</span>(maybeCat)</span><br><span class="line">&#123;</span><br><span class="line">    maybeCat-&gt;<span class="built_in">Moemoe</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当然，在实际的工作中，我们建议把需要 <code>dynamic_cast</code> 后执行的代码，尽量变成虚函数。不过这个已经是另外一个问题了。我们看到，不管是哪种方法都很难避免 <code>if</code> 的存在。而且因为输入数据的类型是模糊的，经常需要强制地、没有任何检查的转换成某个类型，因此很容易出错。</p><p>但是模板与这些方法最大的区别并不在这里。模板无论其参数或者是类型，它都是一个编译期分派的办法。编译期就能确定的东西既可以做类型检查，编译器也能进行优化，砍掉任何不必要的代码执行路径。例如在上例中，</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="function">T <span class="title">addFloatOrMulInt</span><span class="params">(T a, T b)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 迷之代码1：用于T是float的情况</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 迷之代码2：用于T是int时的情况</span></span><br></pre></td></tr></table></figure><p>如果你运用了模板来实现，那么当传入两个不同类型的变量，或者不是 <code>int</code> 和 <code>float</code> 变量，编译器就会提示错误。但是如果使用了我们前述的 <code>Variant</code> 来实现，编译器可就管不了那么多了。但是，成也编译期，败也编译期。最严重的“缺点”，就是你没办法根据用户输入或者别的什么在运行期间可能发生变化的量来决定它产生、或执行什么代码。比如下面的代码段，它是不成立的。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="type">int</span> i, <span class="type">int</span> j&gt;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">foo</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> i + j; &#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cin &gt;&gt; x &gt;&gt; y;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">foo</span>&lt;x, y&gt;();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这点限制也粉碎了妄图用模板来包办工厂（Factory）甚至是反射的梦想。尽管在《Modern C++ Design》中（别问我为什么老举这本书，因为《C++ Templates》和《Generic Programming》我只是囫囵吞枣读过，基本不记得了)大量运用模板来简化工厂方法；同时C++11&#x2F;14中的一些机制如Variadic Template更是让这一问题的解决更加彻底。但无论如何，直到C++11&#x2F;14，光靠模板你就是写不出依靠类名或者ID变量产生类型实例的代码。</p><p>所以说，从能力上来看，模板能做的事情都是编译期完成的。编译期完成的意思就是，当你编译一个程序的时候，所有的量就都已经确定了。比如下面的这个例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">3</span>, b = <span class="number">5</span>;</span><br><span class="line">Variant aVar, bVar;</span><br><span class="line">aVar.<span class="built_in">setInt</span>(a);<span class="comment">// 我们新加上的方法，怎么实现的无所谓，大家明白意思就行了。</span></span><br><span class="line">bVar.<span class="built_in">setInt</span>(b);</span><br><span class="line">Variant result = <span class="built_in">addFloatOrMulInt</span>(aVar, bVar);</span><br></pre></td></tr></table></figure><p>除非世界末日，否则这个例子里不管你怎么蹦跶，单看代码我们就能知道， <code>aVar</code> 和 <code>bVar</code> 都一定会是整数。所以如果有合适的机制，编译器就能知道此处的 <code>addFloatOrMulInt</code> 中只需要执行 <code>Int</code> 路径上的代码，而且编译器在此处也能单独为 <code>Int</code> 路径生成代码，从而去掉那个不必要的 <code>if</code>。</p><p>在模板代码中，这个“合适的机制”就是指“特化”和“部分特化（Partial Specialization）”，后者也叫“偏特化”。</p><h3 id="3-2-2-特化"><a href="#3-2-2-特化" class="headerlink" title="3.2.2. 特化"></a>3.2.2. 特化</h3><p>我的高中物理老师对我说过一句令我受用至今的话：把自己能做的事情做好。编写模板程序也是一样。当你试图用模板解决问题之前，先撇开那些复杂的语法要素，用最直观的方式表达你的需求：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里是伪代码，意思一下</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span>|<span class="function"><span class="type">float</span> <span class="title">addFloatOrMulInt</span><span class="params">(a, b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(type is Int)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> a * b;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (type is Float)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> a + b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">float</span> a, b, c;</span><br><span class="line">    c = <span class="built_in">addFloatOrMulInt</span>(a, b);<span class="comment">// c = a + b;</span></span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> x, y, z;</span><br><span class="line">    z = <span class="built_in">addFloatOrMulInt</span>(x, y);<span class="comment">// z = x * y;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>因为这一节是讲类模板有关的特化和偏特化机制，所以我们不用普通的函数，而是用类的静态成员函数来做这个事情（这就是典型的没事找抽型）：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里仍然是伪代码，意思一下，too。</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span>|<span class="function"><span class="type">float</span> <span class="title">Do</span><span class="params">(a, b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(type is Int)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> a * b;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (type is Float)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">float</span> a, b, c;</span><br><span class="line">    c = AddFloatOrMulInt::<span class="built_in">Do</span>(a, b); <span class="comment">// c = a + b;</span></span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> x, y, z;</span><br><span class="line">    z = AddFloatOrMulInt::<span class="built_in">Do</span>(x, y); <span class="comment">// z = x * y;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>好，意思表达清楚了。我们先从调用方的角度，把这个形式改写一下：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">float</span> a, b, c;</span><br><span class="line">    c = AddFloatOrMulInt&lt;<span class="type">float</span>&gt;::<span class="built_in">Do</span>(a, b); <span class="comment">// c = a + b;</span></span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> x, y, z;</span><br><span class="line">    z = AddFloatOrMulInt&lt;<span class="type">int</span>&gt;::<span class="built_in">Do</span>(x, y); <span class="comment">// z = x * y;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>也许你不明白为什么要改写成现在这个样子。看不懂不怪你，怪我讲得不好。但是你别急，先看看这样改写以后能不能跟我们的目标接近一点。如果我们把 <code>AddFloatOrMulInt&lt;float&gt;::Do</code> 看作一个普通的函数，那么我们可以写两个实现出来：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">float</span> AddFloatOrMulInt&lt;<span class="type">float</span>&gt;::<span class="built_in">Do</span>(<span class="type">float</span> a, <span class="type">float</span> b)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> AddFloatOrMulInt&lt;<span class="type">int</span>&gt;::<span class="built_in">Do</span>(<span class="type">int</span> a, <span class="type">int</span> b)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> a * b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">float</span> a, b, c;</span><br><span class="line">    c = AddFloatOrMulInt&lt;<span class="type">float</span>&gt;::<span class="built_in">Do</span>(a, b); <span class="comment">// c = a + b;</span></span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> x, y, z;</span><br><span class="line">    z = AddFloatOrMulInt&lt;<span class="type">int</span>&gt;::<span class="built_in">Do</span>(x, y); <span class="comment">// z = x * y;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样是不是就很开心了？我们更进一步，把 <code>AddFloatOrMulInt&lt;int&gt;::Do</code> 换成合法的类模板：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这个是给float用的。</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function">T <span class="title">Do</span><span class="params">(T a, T b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> a + b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个是给int用的。</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function">T <span class="title">Do</span><span class="params">(T a, T b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> a * b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="type">float</span> a, b, c;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 嗯，我们需要 c = a + b;</span></span><br><span class="line">    c = AddFloatOrMulInt&lt;<span class="type">float</span>&gt;::<span class="built_in">Do</span>(a, b);</span><br><span class="line">    <span class="comment">// ... 觉得哪里不对劲 ...</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 啊！有两个AddFloatOrMulInt，class看起来一模一样，要怎么区分呢！</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>好吧，问题来了！如何要让两个内容不同，但是模板参数形式相同的类进行区分呢？特化！特化（specialization）是根据一个或多个特殊的整数或类型，给出模板实例化时的一个指定内容。我们先来看特化是怎么应用到这个问题上的。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 首先，要写出模板的一般形式（原型）</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="type">static</span> T <span class="title">Do</span><span class="params">(T a, T b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// 在这个例子里面一般形式里面是什么内容不重要，因为用不上</span></span><br><span class="line">        <span class="comment">// 这里就随便给个0吧。</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">T</span>(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 其次，我们要指定T是int时候的代码，这就是特化：</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span>&lt;<span class="type">int</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">int</span> <span class="title">Do</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> <span class="comment">// </span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> a * b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 再次，我们要指定T是float时候的代码：</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span>&lt;<span class="type">float</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">float</span> <span class="title">Do</span><span class="params">(<span class="type">float</span> a, <span class="type">float</span> b)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> a + b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">// 这里面就不写了</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们再把特化的形式拿出来一瞧：这货有点怪啊： <code>template &lt;&gt; class AddFloatOrMulInt&lt;int&gt;</code>。别急，我给你解释一下。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 我们这个模板的基本形式是什么？</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 但是这个类，是给T是Int的时候用的，于是我们写作</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span>&lt;<span class="type">int</span>&gt;;</span><br><span class="line"><span class="comment">// 当然，这里编译是通不过的。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 但是它又不是个普通类，而是类模板的一个特化（特例）。</span></span><br><span class="line"><span class="comment">// 所以前面要加模板关键字template，</span></span><br><span class="line"><span class="comment">// 以及模板参数列表</span></span><br><span class="line"><span class="keyword">template</span> &lt;/* 这里要填什么？ */&gt; <span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span>&lt;<span class="type">int</span>&gt;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 最后，模板参数列表里面填什么？因为原型的T已经被int取代了。所以这里就不能也不需要放任何额外的参数了。</span></span><br><span class="line"><span class="comment">// 所以这里放空。</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">AddFloatOrMulInt</span>&lt;<span class="type">int</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// ... 针对Int的实现 ... </span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Bingo!</span></span><br></pre></td></tr></table></figure><p>哈，这样就好了。我们来做一个练习。我们有一些类型，然后你要用模板做一个对照表，让类型对应上一个数字。我先来做一个示范：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">-1</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="type">uint8_t</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">0</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>然后呢，你的任务就是，要所有无符号的整数类型的特化（其实就是<code>uint8_t</code>到<code>uint64_t</code>啦），把所有的基本类型都赋予一个ID（当然是不一样的啦）。当你做完后呢，可以把类型所对应的ID打印出来，我仍然以 <code>uint8_t</code> 为例：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintID</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ID of uint8_t: &quot;</span> &lt;&lt; TypeToID&lt;<span class="type">uint8_t</span>&gt;::ID &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>嗯，看起来挺简单的，是吧。但是这里透露出了一个非常重要的信号，我希望你已经能察觉出来了： <code>TypeToID</code> 如同是一个函数。这个函数只能在编译期间执行。它输入一个类型，输出一个ID。</p><p>如果你体味到了这一点，那么恭喜你，你的模板元编程已经开悟了。</p><h3 id="3-2-3-特化：一些其它问题"><a href="#3-2-3-特化：一些其它问题" class="headerlink" title="3.2.3. 特化：一些其它问题"></a>3.2.3. 特化：一些其它问题</h3><p>在上一节结束之后，你一定做了许多的练习。我们再来做三个练习。第一，给<code>float</code>一个ID；第二，给<code>void*</code>一个ID；第三，给任意类型的指针一个ID。先来做第一个:</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="comment">// TypeToID 的模板“原型”</span></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="type">float</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">0xF10A7</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>嗯， 这个你已经了然于心了。那么<code>void*</code>呢？你想了想，这已经是一个复合类型了。不错你还是战战兢兢地写了下来：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="type">void</span>*&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">0x401d</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintID</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ID of uint8_t: &quot;</span> &lt;&lt; TypeToID&lt;<span class="type">void</span>*&gt;::ID &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>遍译运行一下，对了。模板不过如此嘛。然后你觉得自己已经完全掌握了，并试图将所有C++类型都放到模板里面，开始了自我折磨的过程：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ClassB</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="built_in">void</span> ()&gt;;      <span class="comment">// 函数的TypeID</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="type">int</span>[<span class="number">3</span>]&gt;;       <span class="comment">// 数组的TypeID</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="built_in">int</span> (<span class="type">int</span>[<span class="number">3</span>])&gt;; <span class="comment">// 这是以数组为参数的函数的TypeID</span></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="built_in">int</span> (ClassB::*[<span class="number">3</span>])(<span class="type">void</span>*, <span class="type">float</span>[<span class="number">2</span>])&gt;; <span class="comment">// 我也不知道这是什么了，自己看着办吧。</span></span><br></pre></td></tr></table></figure><p>甚至连 <code>const</code> 和 <code>volatile</code> 都能装进去：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="type">int</span> <span class="type">const</span> * <span class="keyword">volatile</span> * <span class="type">const</span> <span class="keyword">volatile</span>&gt;;</span><br></pre></td></tr></table></figure><p>此时就很明白了，只要 <code>&lt;&gt;</code> 内填进去的是一个C++能解析的合法类型，模板都能让你特化。不过这个时候如果你一点都没有写错的话， <code>PrintID</code> 中只打印了我们提供了特化的类型的ID。那如果我们没有为之提供特化的类型呢？比如说double？OK，实践出真知，我们来尝试着运行一下：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintID</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ID of double: &quot;</span> &lt;&lt; TypeToID&lt;<span class="type">double</span>&gt;::ID &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>嗯，它输出的是-1。我们顺藤摸瓜会看到， <code>TypeToID</code>的类模板“原型”的ID是值就是-1。通过这个例子可以知道，当模板实例化时提供的模板参数不能匹配到任何的特化形式的时候，它就会去匹配类模板的“原型”形式。</p><p>不过这里有一个问题要理清一下。和继承不同，类模板的“原型”和它的特化类在实现上是没有关系的，并不是在类模板中写了 <code>ID</code> 这个Member，那所有的特化就必须要加入 <code>ID</code> 这个Member，或者特化就自动有了这个成员。完全没这回事。我们把类模板改成以下形式，或许能看的更清楚一点：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> NotID = <span class="number">-2</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="type">float</span>&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">1</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintID</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ID of float: &quot;</span> &lt;&lt; TypeToID&lt;<span class="type">float</span>&gt;::ID &lt;&lt; endl;       <span class="comment">// Print &quot;1&quot;</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;NotID of float: &quot;</span> &lt;&lt; TypeToID&lt;<span class="type">float</span>&gt;::NotID &lt;&lt; endl; <span class="comment">// Error! TypeToID&lt;float&gt;使用的特化的类，这个类的实现没有NotID这个成员。</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ID of double: &quot;</span> &lt;&lt; TypeToID&lt;<span class="type">double</span>&gt;::ID &lt;&lt; endl;     <span class="comment">// Error! TypeToID&lt;double&gt;是由类模板实例化出来的，它只有NotID，没有ID这个成员。</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样就明白了。类模板和类模板的特化的作用，仅仅是指导编译器选择哪个编译，但是特化之间、特化和它原型的类模板之间，是分别独立实现的。所以如果多个特化、或者特化和对应的类模板有着类似的内容，很不好意思，你得写上若干遍了。</p><p>第三个问题，是写一个模板匹配任意类型的指针。对于C语言来说，因为没有泛型的概念，因此它提供了无类型的指针<code>void*</code>。它的优点是，所有指针都能转换成它。它的缺点是，一旦转换称它后，你就再也不知道这个指针到底是指向<code>float</code>或者是<code>int</code>或者是<code>struct</code>了。</p><p>比如说<code>copy</code>。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">copy</span><span class="params">(<span class="type">void</span>* dst, <span class="type">void</span> <span class="type">const</span>* src, <span class="type">size_t</span> elemSize, <span class="type">size_t</span> elemCount, <span class="type">void</span> (*copyElem)(<span class="type">void</span>* dstElem, <span class="type">void</span> <span class="type">const</span>* srcElem))</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">void</span> <span class="type">const</span>* reader = src;</span><br><span class="line">    <span class="type">void</span> <span class="type">const</span>* writer = dst;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; elemCount; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        copyElem(writer, reader);</span><br><span class="line">        advancePointer(reader, elemSize); <span class="comment">// 把Reader指针往后移动一些字节</span></span><br><span class="line">        advancePointer(writer, elemSize);</span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为什么要提供copyElem，是因为可能有些struct需要深拷贝，所以得用特殊的copy函数。这个在C++98&#x2F;03里面就体现为拷贝构造和赋值函数。</p><p>但是不管怎么搞，因为这个函数的参数只是<code>void*</code>而已，当你使用了错误的elemSize，或者传入了错误的copyElem，就必须要到运行的时候才有可能看出来。注意，这还只是有可能而已。</p><p>那么C++有了模板后，能否既能匹配任意类型的指针，同时又保留了类型信息呢？答案是显然的。至于怎么写，那就得充分发挥你的直觉了：</p><p>首先，我们需要一个<code>typename T</code>来指代“任意类型”这四个字：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br></pre></td></tr></table></figure><p>接下来，我们要写函数原型：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">copy</span><span class="params">(?? dest, ?? src, <span class="type">size_t</span> elemCount)</span></span>;</span><br></pre></td></tr></table></figure><p>这里的 <code>??</code> 要怎么写呢？既然我们有了模板类型参数T，那我们不如就按照经验，写 <code>T*</code> 看看。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">copy</span><span class="params">(T* dst, T <span class="type">const</span>* src, <span class="type">size_t</span> elemCount)</span></span>;</span><br></pre></td></tr></table></figure><p>编译一下，咦，居然通过了。看来这里的语法与我们以前学到的知识并没有什么不同。这也是语言设计最重要的一点原则：一致性。它可以让你辛辛苦苦体验到的规律不至于白费。</p><p>最后就是实现：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">copy</span><span class="params">(T* dst, T <span class="type">const</span>* src, <span class="type">size_t</span> elemCount)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; elemCount; ++i)</span><br><span class="line">    &#123;</span><br><span class="line">        dst[i] = src[i];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>是不是简洁了许多？你不需要再传入size；只要你有正确的赋值函数，也不需要提供定制的copy；也不用担心dst和src的类型不匹配了。</p><p>最后，我们把函数模板学到的东西，也应用到类模板里面：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="comment">// 嗯，需要一个T</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;T*&gt; <span class="comment">// 我要对所有的指针类型特化，所以这里就写T*</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">0x80000000</span>;<span class="comment">// 用最高位表示它是一个指针</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>最后写个例子来测试一下，看看我们的 <code>T*</code> 能不能搞定 <code>float*</code>：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintID</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ID of float*: &quot;</span> &lt;&lt; TypeToID&lt;<span class="type">float</span>*&gt;::ID &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>哈哈，大功告成。嗯，别急着高兴。待我问一个问题：你知道 <code>TypeToID&lt;float*&gt;</code> 后，这里的T是什么吗？换句话说，你知道下面这段代码打印的是什么吗？</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="comment">// TypeToID 的其他代码，略过不表</span></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="comment">// 嗯，需要一个T</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;T*&gt; <span class="comment">// 我要对所有的指针类型特化，所以这里就写T*</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">typedef</span> T SameAsT;</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">0x80000000</span>; <span class="comment">// 用最高位表示它是一个指针</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintID</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ID of float*: &quot;</span> &lt;&lt; TypeToID&lt; TypeToID&lt;<span class="type">float</span>*&gt;::SameAsT &gt;::ID &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>别急着运行，你先猜。</p><p>————————-  这里是给勤于思考的码猴的分割线  ——————————-</p><p>OK，猜出来了吗，T是<code>float</code>。为什么呢？因为你用 <code>float *</code> 匹配了 <code>T *</code>，所以 <code>T</code> 就对应 <code>float</code> 了。没想清楚的自己再多体会一下。</p><p>嗯，所以实际上，我们可以利用这个特性做一件事情：把指针类型的那个指针给“干掉”：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RemovePointer</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">typedef</span> T Result;  <span class="comment">// 如果放进来的不是一个指针，那么它就是我们要的结果。</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RemovePointer</span>&lt;T*&gt;<span class="comment">// 祖传牛皮藓，专治各类指针</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">typedef</span> T Result;  <span class="comment">// 正如我们刚刚讲的，去掉一层指针，把 T* 这里的 T 取出来。</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    RemovePointer&lt;<span class="type">float</span>*&gt;::Result x = <span class="number">5.0f</span>; <span class="comment">// 喏，用RemovePointer后，那个Result就是把float*的指针处理掉以后的结果：float啦。</span></span><br><span class="line">    std::cout &lt;&lt; x &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>当然啦，这里我们实现的不算是真正的 <code>RemovePointer</code>，因为我们只去掉了一层指针。而如果传进来的是类似 <code>RemovePointer&lt;int**&gt;</code> 这样的东西呢？是的没错，去掉一层之后还是一个指针。<code>RemovePointer&lt;int**&gt;::Result</code> 应该是一个 <code>int*</code>，要怎么才能实现我们想要的呢？聪明的你一定能想到：只要像剥洋葱一样，一层一层一层地剥开，不就好了吗！相应地我们应该怎么实现呢？可以把 <code>RemovePointer</code> 的特化版本改成这样（当然如果有一些不明白的地方你可以暂时跳过，接着往下看，很快就会明白的）：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RemovePointer</span>&lt;T*&gt;</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">// 如果是传进来的是一个指针，我们就剥夺一层，直到指针形式不存在为止。</span></span><br><span class="line">    <span class="comment">// 例如 RemovePointer&lt;int**&gt;，Result 是 RemovePointer&lt;int*&gt;::Result，</span></span><br><span class="line">    <span class="comment">// 而 RemovePointer&lt;int*&gt;::Result 又是 int，最终就变成了我们想要的 int，其它也是类似。</span></span><br><span class="line">    <span class="keyword">typedef</span> <span class="keyword">typename</span> RemovePointer&lt;T&gt;::Result Result;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>是的没错，这便是我们想要的 <code>RemovePointer</code> 的样子。类似的你还可以试着实现 <code>RemoveConst</code>, <code>AddPointer</code> 之类的东西。</p><p>OK，回到我们之前的话题，如果这个时候，我需要给 <code>int*</code> 提供一个更加特殊的特化，那么我还得多提供一个：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="comment">// TypeToID 的其他代码，略过不表</span></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="comment">// 嗯，需要一个T</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;T*&gt;    <span class="comment">// 我要对所有的指针类型特化，所以这里就写T*</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">typedef</span> T SameAsT;</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">0x80000000</span>; <span class="comment">// 用最高位表示它是一个指针</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;&gt; <span class="comment">// 嗯，int* 已经是个具体的不能再具体的类型了，所以模板不需要额外的类型参数了</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">TypeToID</span>&lt;<span class="type">int</span>*&gt; <span class="comment">// 嗯，对int*的特化。在这里呢，要把int*整体看作一个类型</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">static</span> <span class="type">int</span> <span class="type">const</span> ID = <span class="number">0x12345678</span>; <span class="comment">// 给一个缺心眼的ID</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintID</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;ID of int*: &quot;</span> &lt;&lt; TypeToID&lt;<span class="type">int</span>*&gt;::ID &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>嗯，这个时候它会输出0x12345678的十进制（大概？）。<br>可能会有较真的人说，<code>int*</code> 去匹配 <code>T</code> 或者 <code>T*</code>，也是合法的。就和你说22岁以上能结婚，那24岁当然也能结婚一样。<br>那为什么 <code>int*</code> 就会找 <code>int*</code>，<code>float *</code>因为没有合适的特化就去找 <code>T*</code>，更一般的就去找 <code>T</code> 呢？废话，有专门为你准备的东西你不用，非要自己找事？这就是直觉。<br>但是呢，直觉对付更加复杂的问题还是没用的（也不是没用，主要是你没这个直觉了）。我们要把这个直觉，转换成合理的规则——即模板的匹配规则。<br>当然，这个匹配规则是对复杂问题用的，所以我们会到实在一眼看不出来的时候才会动用它。一开始我们只要把握：<strong>模板是从最特殊到最一般形式进行匹配的</strong> 就可以了。</p><h2 id="3-3-即用即推导"><a href="#3-3-即用即推导" class="headerlink" title="3.3. 即用即推导"></a>3.3. 即用即推导</h2><h3 id="3-3-1-视若无睹的语法错误"><a href="#3-3-1-视若无睹的语法错误" class="headerlink" title="3.3.1. 视若无睹的语法错误"></a>3.3.1. 视若无睹的语法错误</h3><p>这一节我们将讲述模板一个非常重要的行为特点：那就是什么时候编译器会对模板进行推导，推导到什么程度。</p><p>这一知识，对于理解模板的编译期行为、以及修正模板编译错误都非常重要。</p><p>我们先来看一个例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">X</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">Y</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">typedef</span> X&lt;T&gt; ReboundType;        <span class="comment">// 类型定义1</span></span><br><span class="line">    <span class="keyword">typedef</span> <span class="keyword">typename</span> X&lt;T&gt;::MemberType MemberType;<span class="comment">// 类型定义2</span></span><br><span class="line">    <span class="keyword">typedef</span> UnknownType MemberType3;    <span class="comment">// 类型定义3</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        X&lt;T&gt; instance0;</span><br><span class="line">        <span class="keyword">typename</span> X&lt;T&gt;::MemberType instance1;</span><br><span class="line">        WTF instance2</span><br><span class="line">        大王叫我来巡山 - + &amp;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>把这段代码编译一下，类型定义3出错，其它的都没问题。不过到这里你应该会有几个问题：</p><ol><li>不是<code>struct X&lt;T&gt;</code>的定义是空的吗？为什么在<code>struct Y</code>内的类型定义2使用了 <code>X&lt;T&gt;::MemberType</code> 编译器没有报错？</li><li>类型定义2中的<code>typename</code>是什么鬼？为什么类型定义1就不需要？</li><li>为什么类型定义3会导致编译错误？</li><li>为什么<code>void foo()</code>在MSVC下什么错误都没报？</li></ol><p>这时我们就需要请出C++11标准 —— 中的某些概念了。这是我们到目前为止第一次参阅标准。我希望能尽量减少直接参阅标准的次数，因此即便是极为复杂的模板匹配决议我都暂时没有引入标准中的描述。<br>然而，Template引入的“双阶段名称查找（Two phase name lookup）”堪称是C++中最黑暗的角落 —— 这是LLVM的团队自己在博客上说的 —— 因此在这里，我们还是有必要去了解标准中是如何规定的。</p><h3 id="3-3-2-名称查找：I-am-who-I-am"><a href="#3-3-2-名称查找：I-am-who-I-am" class="headerlink" title="3.3.2. 名称查找：I am who I am"></a>3.3.2. 名称查找：I am who I am</h3><p>在C++标准中对于“名称查找（name lookup）”这个高大上的名词的诠释，主要集中出现在三处。第一处是3.4节，标题名就叫“Name Lookup”；第二处在10.2节，继承关系中的名称查找；第三处在14.6节，名称解析（name resolution）。</p><p>名称查找&#x2F;名称解析，是编译器的基石。对编译原理稍有了解的人，都知道“符号表”的存在及重要意义。考虑一段最基本的C代码：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="type">int</span> b;</span><br><span class="line">b = (a + <span class="number">1</span>) * <span class="number">2</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;Result: %d&quot;</span>, b);</span><br></pre></td></tr></table></figure><p>在这段代码中，所有出现的符号可以分为以下几类：</p><ul><li><code>int</code>：类型标识符，代表整型；</li><li><code>a</code>, <code>b</code>, <code>printf</code>：变量名或函数名；</li><li><code>=</code>, <code>+</code>, <code>*</code>：运算符；</li><li><code>,</code>, <code>;</code>, <code>(</code>, <code>)</code>：分隔符；</li></ul><p>那么，编译器怎么知道<code>int</code>就是整数类型，<code>b=(a+1)*2</code>中的<code>a</code>和<code>b</code>就是整型变量呢？这就是名称查找&#x2F;名称解析的作用：它告诉编译器，这个标识符（identifer）是在哪里被声明或定义的，它究竟是什么意思。</p><p>也正因为这个机制非常基础，所以它才会面临各种可能的情况，编译器也要想尽办法让它在大部分场合都表现的合理。比如我们常见的作用域规则，就是为了对付名称在不同代码块中传播、并且遇到重名要如何处理的问题。下面是一个最简单的、大家在语言入门过程中都会碰到的一个例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">f</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">0</span>;</span><br><span class="line">    a += <span class="number">2</span>;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Inside &lt;a&gt;: %d\n&quot;</span>, a);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">g</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Outside &lt;a&gt;: %d\n&quot;</span>, a);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">f</span>();</span><br><span class="line">    <span class="built_in">g</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ------------ Console Output -----------------</span></span><br><span class="line"><span class="comment">Inside &lt;a&gt;: 2</span></span><br><span class="line"><span class="comment">Outside &lt;a&gt;: 0</span></span><br><span class="line"><span class="comment">--------------- Console Output -------------- */</span></span><br></pre></td></tr></table></figure><p>我想大家尽管不能处理所有名称查找中所遇到的问题，但是对一些常见的名称查找规则也有了充分的经验，可以解决一些常见的问题。<br>但是模板的引入，使得名称查找这一本来就不简单的基本问题变得更加复杂了。<br>考虑下面这个例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">A</span>  &#123; <span class="type">int</span> a; &#125;;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">AB</span> &#123; <span class="type">int</span> a, b; &#125;;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">C</span>  &#123; <span class="type">int</span> c; &#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="built_in">foo</span>(T&amp; v0, C&amp; v1)&#123;</span><br><span class="line">    v0.a = <span class="number">1</span>;</span><br><span class="line">    v1.a = <span class="number">2</span>;</span><br><span class="line">    v1.c = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>简单分析上述代码很容易得到以下结论：</p><ol><li>函数<code>foo</code>中的变量<code>v1</code>已经确定是<code>struct C</code>的实例，所以，<code>v1.a = 2;</code>会导致编译错误，<code>v1.c = 3;</code>是正确的代码；</li><li>对于变量<code>v0</code>来说，这个问题就变得很微妙。如果<code>v0</code>是<code>struct A</code>或者<code>struct AB</code>的实例，那么<code>foo</code>中的语句<code>v0.a = 1;</code>就是正确的。如果是<code>struct C</code>，那么这段代码就是错误的。</li></ol><p>因此在模板定义的地方进行语义分析，并不能<strong>完全</strong>得出代码是正确或者错误的结论，只有到了实例化阶段，确定了模板参数的类型后，才知道这段代码正确与否。令人高兴的是，在这一问题上，我们和C++标准委员会的见地一致，说明我们的C++水平已经和Herb Sutter不分伯仲了。既然我们和Herb Sutter水平差不多，那凭什么人家就吃香喝辣？下面我们来选几条标准看看服不服：</p><blockquote><p><strong>14.6 名称解析（Name resolution）</strong></p></blockquote><blockquote><p><strong>1)</strong> 模板定义中能够出现以下三类名称：</p></blockquote><blockquote><ul><li>模板名称、或模板实现中所定义的名称；</li><li>和模板参数有关的名称；</li><li>模板定义所在的定义域内能看到的名称。</li></ul></blockquote><blockquote><p>…</p></blockquote><blockquote><p><strong>9)</strong> … 如果名字查找和模板参数有关，那么查找会延期到模板参数全都确定的时候。 …</p></blockquote><blockquote><p><strong>10)</strong> 如果（模板定义内出现的）名字和模板参数无关，那么在模板定义处，就应该找得到这个名字的声明。…</p></blockquote><blockquote><p><strong>14.6.2 依赖性名称（Dependent names）</strong></p></blockquote><blockquote><p><strong>1)</strong> …（模板定义中的）表达式和类型可能会依赖于模板参数，并且模板参数会影响到名称查找的作用域 …  如果表达式中有操作数依赖于模板参数，那么整个表达式都依赖于模板参数，名称查找延期到<strong>模板实例化时</strong>进行。并且定义时和实例化时的上下文都会参与名称查找。（依赖性）表达式可以分为类型依赖（类型指模板参数的类型）或值依赖。</p></blockquote><blockquote><p><strong>14.6.2.2 类型依赖的表达式</strong></p></blockquote><blockquote><p><strong>2)</strong> 如果成员函数所属的类型是和模板参数有关的，那么这个成员函数中的<code>this</code>就认为是类型依赖的。</p></blockquote><blockquote><p><strong>14.6.3 非依赖性名称（Non-dependent names）</strong></p></blockquote><blockquote><p><strong>1)</strong> 非依赖性名称在<strong>模板定义</strong>时使用通常的名称查找规则进行名称查找。</p></blockquote><p>[Working Draft: Standard of Programming Language C++, N3337][1]</p><p>知道差距在哪了吗：人家会说黑话。什么时候咱们也会说黑话了，就是标准委员会成员了，反正懂得也不比他们少。不过黑话确实不太好懂 —— 怪我翻译不好的人，自己看原文，再说好懂了人家还靠什么吃饭 —— 我们来举一个例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">B</span> &#123; <span class="type">int</span> v; &#125;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">X</span> &#123;</span><br><span class="line">    B b;                  <span class="comment">// B 是第三类名字，b 是第一类</span></span><br><span class="line">    T t;                  <span class="comment">// T 是第二类</span></span><br><span class="line">    X* anthor;            <span class="comment">// X 这里代指 X&lt;T&gt;，第一类</span></span><br><span class="line">    <span class="keyword">typedef</span> <span class="type">int</span> Y;        <span class="comment">// int 是第三类</span></span><br><span class="line">    Y y;                  <span class="comment">// Y 是第一类</span></span><br><span class="line">    C c;                  <span class="comment">// C 什么都不是，编译错误。</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span> </span>&#123;</span><br><span class="line">       b.v += y;          <span class="comment">// b 是第一类，非依赖性名称</span></span><br><span class="line">       b.v *= T::s_mem;   <span class="comment">// T::s_mem 是第二类</span></span><br><span class="line">                          <span class="comment">// s_mem的作用域由T决定</span></span><br><span class="line">                          <span class="comment">// 依赖性名称，类型依赖</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>所以，按照标准的意思，名称查找会在模板定义和实例化时各做一次，分别处理非依赖性名称和依赖性名称的查找。这就是“两阶段名称查找”这一名词的由来。只不过这个术语我也不知道是谁发明的，它并没有出现的标准上，但是频繁出现在StackOverflow和Blog上。</p><p>接下来，我们就来解决2.3.1节中留下的几个问题。</p><p>先看第四个问题。为什么MSVC中，函数模板的定义内不管填什么编译器都不报错？因为MSVC在分析模板中成员函数定义时没有做任何事情。至于为啥连“大王叫我来巡山”都能过得去，这是C++语法&#x2F;语义分析的特殊性导致的。<br>C++是个非常复杂的语言，以至于它的编译器，不可能通过词法-语法-语义多趟分析清晰分割，因为它的语义将会直接干扰到语法：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span>&#123;</span><br><span class="line">    A&lt;T&gt; b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这段简短的代码中，就包含了两个歧义的可能，一是<code>A</code>是模板，于是<code>A&lt;T&gt;</code>是一个实例化的类型，<code>b</code>是变量，另外一种是比较表达式（Comparison Expression）的组合，<code>((A &lt; T) &gt; b)</code>。</p><p>甚至词法分析也会受到语义的干扰，C++11中才明确被修正的<code>vector&lt;vector&lt;int&gt;&gt;</code>，就因为<code>&gt;&gt;</code>被误解为右移或流操作符，而导致某些编译器上的错误。因此，在语义没有确定之前，连语法都没有分析的价值。</p><p>大约是基于如此考量，为了偷懒，MSVC将包括所有模板成员函数的语法&#x2F;语义分析工作都挪到了第二个Phase，于是乎连带着语法分析都送进了第二个阶段。符合标准么？显然不符合。</p><p>但是这里值得一提的是，MSVC的做法和标准相比，虽然投机取巧，但并非有弊无利。我们来先说一说坏处。考虑以下例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ----------- X.h ------------</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">X</span> &#123;</span><br><span class="line">    <span class="comment">// 实现代码</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ---------- X.cpp -----------</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ... 一些代码 ...</span></span><br><span class="line">X&lt;<span class="type">int</span>&gt; xi; </span><br><span class="line"><span class="comment">// ... 一些代码 ...</span></span><br><span class="line">X&lt;<span class="type">float</span>&gt; xf;</span><br><span class="line"><span class="comment">// ... 一些代码 ...</span></span><br></pre></td></tr></table></figure><p>此时如果X中有一些与模板参数无关的错误，如果名称查找&#x2F;语义分析在两个阶段完成，那么这些错误会很早、且唯一的被提示出来；但是如果一切都在实例化时处理，那么可能会导致不同的实例化过程提示同样的错误。而模板在运用过程中，往往会产生很多实例，此时便会大量报告同样的错误。</p><p>当然，MSVC并不会真的这么做。根据推测，最终他们是合并了相同的错误。因为即便对于模板参数相关的编译错误，也只能看到最后一次实例化的错误信息：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">X</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">Y</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">typedef</span> X&lt;T&gt; ReboundType; <span class="comment">// 类型定义1</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">foo</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        X&lt;T&gt; instance0;</span><br><span class="line">        X&lt;T&gt;::MemberType instance1;</span><br><span class="line">        WTF instance2</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">poo</span><span class="params">()</span></span>&#123;</span><br><span class="line">    Y&lt;<span class="type">int</span>&gt;::<span class="built_in">foo</span>();</span><br><span class="line">    Y&lt;<span class="type">float</span>&gt;::<span class="built_in">foo</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>MSVC下和模板相关的错误只有一个：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">error C2039: &#x27;MemberType&#x27;: is not a member of &#x27;X&lt;T&gt;&#x27;</span><br><span class="line">          with</span><br><span class="line">          [</span><br><span class="line">              T=float</span><br><span class="line">          ]</span><br></pre></td></tr></table></figure><p>然后是一些语法错误，比如<code>MemberType</code>不是一个合法的标识符之类的。这样甚至你会误以为<code>int</code>情况下模板的实例化是正确的。虽然在有了经验之后会发现这个问题挺荒唐的，但是仍然会让新手有困惑。</p><p>相比之下，更加遵守标准的Clang在错误提示上就要清晰许多：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">error: unknown type name &#x27;WTF&#x27;</span><br><span class="line">    WTF instance2</span><br><span class="line">    ^</span><br><span class="line">error: expected &#x27;;&#x27; at end of declaration</span><br><span class="line">    WTF instance2</span><br><span class="line">                 ^</span><br><span class="line">                 ;</span><br><span class="line">error: no type named &#x27;MemberType&#x27; in &#x27;X&lt;int&gt;&#x27;</span><br><span class="line">    typename X&lt;T&gt;::MemberType instance1;</span><br><span class="line">    ~~~~~~~~~~~~~~~^~~~~~~~~~</span><br><span class="line">    note: in instantiation of member function &#x27;Y&lt;int&gt;::foo&#x27; requested here</span><br><span class="line">        Y&lt;int&gt;::foo();</span><br><span class="line">                ^</span><br><span class="line">error: no type named &#x27;MemberType&#x27; in &#x27;X&lt;float&gt;&#x27;</span><br><span class="line">    typename X&lt;T&gt;::MemberType instance1;</span><br><span class="line">    ~~~~~~~~~~~~~~~^~~~~~~~~~</span><br><span class="line">    note: in instantiation of member function &#x27;Y&lt;float&gt;::foo&#x27; requested here</span><br><span class="line">        Y&lt;float&gt;::foo();</span><br><span class="line">                  ^</span><br><span class="line">4 errors generated.</span><br></pre></td></tr></table></figure><p>可以看到，Clang的提示和标准更加契合。它很好地区分了模板在定义和实例化时分别产生的错误。</p><p>另一个缺点也与之类似。因为没有足够的检查，如果你写的模板没有被实例化，那么很可能缺陷会一直存在于代码之中。特别是模板代码多在头文件。虽然不如接口那么重要，但也是属于被公开的部分，别人很可能会踩到坑上。缺陷一旦传播开修复起来就没那么容易了。</p><p>但是正如我前面所述，这个违背了标准的特性，并不是一无是处。首先，它可以完美的兼容标准。符合标准的、能够被正确编译的代码，一定能够被MSVC的方案所兼容。其次，它带来了一个非常有趣的特性，看下面这个例子：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">A</span>;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">X</span> &#123;</span><br><span class="line">    <span class="type">int</span> v;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">convertTo</span><span class="params">(A&amp; a)</span> </span>&#123;</span><br><span class="line">       a.v = v; <span class="comment">// 这里需要A的实现</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">A</span> &#123; <span class="type">int</span> v; &#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    X&lt;<span class="type">int</span>&gt; x;</span><br><span class="line">    x.<span class="built_in">foo</span>(<span class="number">5</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个例子在Clang中是错误的，因为：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">error: variable has incomplete type &#x27;A&#x27;</span><br><span class="line">                        A a;</span><br><span class="line">                          ^</span><br><span class="line">    note: forward declaration of &#x27;A&#x27;</span><br><span class="line">     struct A;</span><br><span class="line">            ^</span><br><span class="line">1 error generated.</span><br></pre></td></tr></table></figure><p>符合标准的写法需要将类模板的定义，和函数模板的定义分离开：</p><blockquote><p>TODO 此处例子不够恰当，并且描述有歧义。需要在未来版本中修订。</p></blockquote><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">A</span>;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">X</span> &#123;</span><br><span class="line">    <span class="type">int</span> v;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">convertTo</span><span class="params">(A&amp; a)</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">A</span> &#123; <span class="type">int</span> v; &#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="type">void</span> X&lt;T&gt;::<span class="built_in">convertTo</span>(A&amp; a) &#123;</span><br><span class="line">   a.v = v;</span><br><span class="line">&#125;</span><br><span class="line">    </span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    X&lt;<span class="type">int</span>&gt; x;</span><br><span class="line">    x.<span class="built_in">foo</span>(<span class="number">5</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但是其实我们知道，<code>foo</code>要到实例化之后，才需要真正的做语义分析。在MSVC上，因为函数实现就是到模板实例化时才处理的，所以这个例子是完全正常工作的。因此在上面这个例子中，MSVC的实现要比标准更加易于写和维护，是不是有点写Java&#x2F;C那种声明实现都在同一处的清爽感觉了呢！</p><p>扩展阅读： [The Dreaded Two-Phase Name Lookup][2]</p><h3 id="3-3-3-“多余的”-typename-关键字"><a href="#3-3-3-“多余的”-typename-关键字" class="headerlink" title="3.3.3. “多余的”  typename 关键字"></a>3.3.3. “多余的”  typename 关键字</h3><p>到了这里，2.3.1 中提到的四个问题，还有三个没有解决：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">X</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">Y</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">typedef</span> X&lt;T&gt; ReboundType;<span class="comment">// 这里为什么是正确的？</span></span><br><span class="line">    <span class="keyword">typedef</span> <span class="keyword">typename</span> X&lt;T&gt;::MemberType MemberType2;<span class="comment">// 这里的typename是做什么的？</span></span><br><span class="line">    <span class="keyword">typedef</span> UnknownType MemberType3;<span class="comment">// 这里为什么会出错？</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>我们运用我们2.3.2节中学习到的标准，来对Y内部做一下分析：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">Y</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// X可以查找到原型；</span></span><br><span class="line">    <span class="comment">// X&lt;T&gt;是一个依赖性名称，模板定义阶段并不管X&lt;T&gt;是不是正确的。</span></span><br><span class="line">    <span class="keyword">typedef</span> X&lt;T&gt; ReboundType;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// X可以查找到原型；</span></span><br><span class="line">    <span class="comment">// X&lt;T&gt;是一个依赖性名称，X&lt;T&gt;::MemberType也是一个依赖性名称；</span></span><br><span class="line">    <span class="comment">// 所以模板声明时也不会管X模板里面有没有MemberType这回事。</span></span><br><span class="line">    <span class="keyword">typedef</span> <span class="keyword">typename</span> X&lt;T&gt;::MemberType MemberType2;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// UnknownType 不是一个依赖性名称</span></span><br><span class="line">    <span class="comment">// 而且这个名字在当前作用域中不存在，所以直接报错。</span></span><br><span class="line">    <span class="keyword">typedef</span> UnknownType MemberType3;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>下面，唯一的问题就是第二个：<code>typename</code>是做什么的？</p><p>对于用户来说，这其实是一个语法噪音。也就是说，其实就算没有它，语法上也说得过去。事实上，某些情况下MSVC的确会在标准需要的时候，不用写<code>typename</code>。但是标准中还是规定了形如 <code>T::MemberType</code> 这样的<code>qualified id</code> 在默认情况下不是一个类型，而是解释为<code>T</code>的一个成员变量<code>MemberType</code>，只有当<code>typename</code>修饰之后才能作为类型出现。</p><p>事实上，标准对<code>typename</code>的使用规定极为复杂，也算是整个模板中的难点之一。如果想了解所有的标准，需要阅读标准14.6节下2-7条，以及14.6.2.1第一条中对于<code>current instantiation</code>的解释。</p><p>简单来说，如果编译器能在出现的时候知道它是一个类型，那么就不需要<code>typename</code>，如果必须要到实例化的时候才能知道它是不是合法，那么定义的时候就把这个名称作为变量而不是类型。</p><p>我们用一行代码来说明这个问题：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">a * b;</span><br></pre></td></tr></table></figure><p>在没有模板的情况下，这个语句有两种可能的意思：如果<code>a</code>是一个类型，这就是定义了一个指针<code>b</code>，它拥有类型<code>a*</code>；如果<code>a</code>是一个对象或引用，这就是计算一个表达式<code>a*b</code>，虽然结果并没有保存下来。可是如果上面的<code>a</code>是模板参数的成员，会发生什么呢？</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="function"><span class="type">void</span> <span class="title">meow</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    T::a * b; <span class="comment">// 这是指针定义还是表达式语句？</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>编译器对模板进行语法检查的时候，必须要知道上面那一行到底是个什么——这当然可以推迟到实例化的时候进行（比如VC，这也是上面说过VC可以不加<code>typename</code>的原因），不过那是另一个故事了——显然在模板定义的时候，编译器并不能妄断。因此，C++标准规定，在没有<code>typename</code>约束的情况下认为这里<code>T::a</code>不是类型，因此<code>T::a * b;</code> 会被当作表达式语句（例如乘法）；而为了告诉编译器这是一个指针的定义，我们必须在<code>T::a</code>之前加上<code>typename</code>关键字，告诉编译器<code>T::a</code>是一个类型，这样整个语句才能符合指针定义的语法。</p><p>在这里，我举几个例子帮助大家理解<code>typename</code>的用法，这几个例子已经足以涵盖日常使用[（预览）][3]：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">A</span>;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">B</span>;</span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">typename</span> T&gt; <span class="keyword">struct</span> <span class="title class_">X</span> &#123;</span><br><span class="line">    <span class="keyword">typedef</span> X&lt;T&gt; TA; <span class="comment">// 编译器当然知道 X&lt;T&gt; 是一个类型。</span></span><br><span class="line">    <span class="keyword">typedef</span> X    TB; <span class="comment">// X 等价于 X&lt;T&gt; 的缩写</span></span><br><span class="line">    <span class="keyword">typedef</span> T    TC; <span class="comment">// T 不是一个类型还玩毛</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ！！！注意我要变形了！！！</span></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Y</span> &#123;</span><br><span class="line">        <span class="keyword">typedef</span> X&lt;T&gt;     TD;          <span class="comment">// X 的内部，既然外部高枕无忧，内部更不用说了</span></span><br><span class="line">        <span class="keyword">typedef</span> X&lt;T&gt;::Y  TE;          <span class="comment">// 嗯，这里也没问题，编译器知道Y就是当前的类型，</span></span><br><span class="line">                                      <span class="comment">// 这里在VS2015上会有错，需要添加 typename，</span></span><br><span class="line">                                      <span class="comment">// Clang 上顺利通过。</span></span><br><span class="line">        <span class="keyword">typedef</span> <span class="keyword">typename</span> X&lt;T*&gt;::Y TF; <span class="comment">// 这个居然要加 typename！</span></span><br><span class="line">                                      <span class="comment">// 因为，X&lt;T*&gt;和X&lt;T&gt;不一样哦，</span></span><br><span class="line">                                      <span class="comment">// 它可能会在实例化的时候被别的偏特化给抢过去实现了。</span></span><br><span class="line">    &#125;;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">typedef</span> A TG;                   <span class="comment">// 嗯，没问题，A在外面声明啦</span></span><br><span class="line">    <span class="keyword">typedef</span> B&lt;T&gt; TH;                <span class="comment">// B&lt;T&gt;也是一个类型</span></span><br><span class="line">    <span class="keyword">typedef</span> <span class="keyword">typename</span> B&lt;T&gt;::type TI; <span class="comment">// 嗯，因为不知道B&lt;T&gt;::type的信息，</span></span><br><span class="line">                                    <span class="comment">// 所以需要typename</span></span><br><span class="line">    <span class="keyword">typedef</span> B&lt;<span class="type">int</span>&gt;::type TJ;        <span class="comment">// B&lt;int&gt; 不依赖模板参数，</span></span><br><span class="line">                                    <span class="comment">// 所以编译器直接就实例化（instantiate）了</span></span><br><span class="line">                                    <span class="comment">// 但是这个时候，B并没有被实现，所以就出错了</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h2 id="3-4-本章小结"><a href="#3-4-本章小结" class="headerlink" title="3.4. 本章小结"></a>3.4. 本章小结</h2><p>这一章是写作中最艰难的一章，中间停滞了将近一年。因为要说清楚C++模板中一些语法噪音和设计决议并不是一件轻松的事情。不过通过这一章的学习，我们知道了下面这几件事情：</p><ol><li><p><strong>部分特化&#x2F;偏特化</strong> 和 <strong>特化</strong> 相当于是模板实例化过程中的<code>if-then-else</code>。这使得我们根据不同类型，选择不同实现的需求得以实现；</p></li><li><p>在 2.3.3 一节我们插入了C++模板中最难理解的内容之一：名称查找。名称查找是语义分析的一个环节，模板内书写的 <strong>变量声明</strong>、<strong>typedef</strong>、<strong>类型名称</strong> 甚至 <strong>类模板中成员函数的实现</strong> 都要符合名称查找的规矩才不会出错；</p></li><li><p>C++编译器对语义的分析的原则是“大胆假设，小心求证”：在能求证的地方尽量求证 —— 比如两段式名称查找的第一阶段；无法检查的地方假设你是正确的 —— 比如<code>typedef typename A&lt;T&gt;::MemberType X;</code>在模板定义时因为<code>T</code>不明确不会轻易判定这个语句的死刑。</p></li></ol><p>从下一章开始，我们将进入元编程环节。我们将使用大量的示例，一方面帮助巩固大家学到的模板知识，一方面也会引导大家使用函数式思维去解决常见的问题。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;3-模板元编程基础&quot;&gt;&lt;a href=&quot;#3-模板元编程基础&quot; class=&quot;headerlink&quot; title=&quot;3. 模板元编程基础&quot;&gt;&lt;/a&gt;3. 模板元编程基础&lt;/h1&gt;&lt;h2 id=&quot;3-1-编程，元编程，模板元编程&quot;&gt;&lt;a href=&quot;#3-1-编程，</summary>
      
    
    
    
    
    <category term="c++ template" scheme="https://kettycode.github.io/tags/c-template/"/>
    
  </entry>
  
  <entry>
    <title>现代C++学习-其他杂项</title>
    <link href="https://kettycode.github.io/2024/02/06/cpp/%E7%8E%B0%E4%BB%A3C++/base10/"/>
    <id>https://kettycode.github.io/2024/02/06/cpp/%E7%8E%B0%E4%BB%A3C++/base10/</id>
    <published>2024-02-06T04:59:59.000Z</published>
    <updated>2024-03-10T09:21:31.277Z</updated>
    
    <content type="html"><![CDATA[<h1 id="其他杂项"><a href="#其他杂项" class="headerlink" title="其他杂项"></a>其他杂项</h1><h2 id="1-新类型-long-long-int"><a href="#1-新类型-long-long-int" class="headerlink" title="1.新类型 long long int"></a>1.新类型 long long int</h2><p>long long int 并不是 C++11 最先引入的，其实早在 C99， long long int 就已经被纳入 C 标准中，所以大部分的编译器早已支持。 C++11 的工作则是正式把它纳入标准库， 规定了一个 long long int 类型至少具备 64 位的比特数</p><h2 id="2-noexcept的修饰和操作"><a href="#2-noexcept的修饰和操作" class="headerlink" title="2.noexcept的修饰和操作"></a>2.noexcept的修饰和操作</h2><p>C++11 将异常的声明简化为以下两种情况：<br>1.函数可能抛出任何异常<br>2.函数不能抛出任何异常<br>并使用 noexcept 对这两种行为进行限制</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">may_throw</span><span class="params">()</span></span>; <span class="comment">// 可能抛出异常</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">no_throw</span><span class="params">()</span> <span class="keyword">noexcept</span></span>; <span class="comment">// 不可能抛出异常</span></span><br></pre></td></tr></table></figure><p>使用 noexcept 修饰过的函数如果抛出异常，编译器会使用 std::terminate() 来立即终止程序运行。</p><p>noexcept 还能够做操作符，用于操作一个表达式，当表达式无异常时，返回 true，否则返回 false</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">may_throw</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">auto</span> non_block_throw = []&#123;</span><br><span class="line">    <span class="built_in">may_throw</span>();</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">no_throw</span><span class="params">()</span> <span class="keyword">noexcept</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> block_throw = []() <span class="keyword">noexcept</span> &#123;</span><br><span class="line">    <span class="built_in">no_throw</span>();</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::cout &lt;&lt; std::boolalpha</span><br><span class="line">        &lt;&lt; <span class="string">&quot;may_throw() noexcept? &quot;</span> &lt;&lt; <span class="built_in">noexcept</span>(<span class="built_in">may_throw</span>()) &lt;&lt; std::endl</span><br><span class="line">        &lt;&lt; <span class="string">&quot;no_throw() noexcept? &quot;</span> &lt;&lt; <span class="built_in">noexcept</span>(<span class="built_in">no_throw</span>()) &lt;&lt; std::endl</span><br><span class="line">        &lt;&lt; <span class="string">&quot;lmay_throw() noexcept? &quot;</span> &lt;&lt; <span class="built_in">noexcept</span>(<span class="built_in">non_block_throw</span>()) &lt;&lt; std::endl</span><br><span class="line">        &lt;&lt; <span class="string">&quot;lno_throw() noexcept? &quot;</span> &lt;&lt; <span class="built_in">noexcept</span>(<span class="built_in">block_throw</span>()) &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>noexcept 修饰完一个函数之后能够起到封锁异常扩散的功效，如果内部产生异常，外部也不会触发</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="built_in">may_throw</span>();</span><br><span class="line">&#125; <span class="built_in">catch</span> (...) &#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;捕获异常, 来自 may_throw()&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="built_in">non_block_throw</span>();</span><br><span class="line">&#125; <span class="built_in">catch</span> (...) &#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;捕获异常, 来自 non_block_throw()&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="built_in">block_throw</span>();</span><br><span class="line">&#125; <span class="built_in">catch</span> (...) &#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="string">&quot;捕获异常, 来自 block_throw()&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">捕获异常, 来自 <span class="built_in">may_throw</span>()</span><br><span class="line">捕获异常, 来自 <span class="built_in">non_block_throw</span>()</span><br></pre></td></tr></table></figure><h2 id="3-字面量"><a href="#3-字面量" class="headerlink" title="3.字面量"></a>3.字面量</h2><h3 id="原始字符串字面量"><a href="#原始字符串字面量" class="headerlink" title="原始字符串字面量"></a>原始字符串字面量</h3><p>C++11 提供了原始字符串字面量的写法，可以在一个字符串前方使用 R 来修饰这个字符串， 同时，将原始字符串使用括号包裹</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    std::string str = <span class="string">R&quot;(C:\File\To\Path)&quot;</span>;</span><br><span class="line">    std::cout &lt;&lt; str &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="自定义字面量"><a href="#自定义字面量" class="headerlink" title="自定义字面量"></a>自定义字面量</h3><p>C++11 引进了自定义字面量的能力，通过重载双引号后缀运算符实现</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 字符串字面量自定义必须设置如下的参数列表</span></span><br><span class="line">std::string <span class="keyword">operator</span><span class="string">&quot;&quot;</span> _wow1(<span class="type">const</span> <span class="type">char</span> *wow1, <span class="type">size_t</span> len) &#123;</span><br><span class="line">    <span class="keyword">return</span> std::<span class="built_in">string</span>(wow1)+<span class="string">&quot;woooooooooow, amazing&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">std::string <span class="keyword">operator</span><span class="string">&quot;&quot;</span> _wow2 (<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> i) &#123;</span><br><span class="line">    <span class="keyword">return</span> std::<span class="built_in">to_string</span>(i)+<span class="string">&quot;woooooooooow, amazing&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> str = <span class="string">&quot;abc&quot;</span>_wow1;</span><br><span class="line">    <span class="keyword">auto</span> num = <span class="number">1</span>_wow2;</span><br><span class="line">    std::cout &lt;&lt; str &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; num &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>自定义字面量支持四种字面量：<br>1.整型字面量：重载时必须使用 unsigned long long、const char *、模板字面量算符参数，在上面的代码中使用的是前者；<br>2.浮点型字面量：重载时必须使用 long double、const char *、模板字面量算符；<br>3.字符串字面量：必须使用 (const char *, size_t) 形式的参数表；<br>4.字符字面量：参数只能是 char, wchar_t, char16_t, char32_t 这几种类型</p><h2 id="4-内存对齐"><a href="#4-内存对齐" class="headerlink" title="4.内存对齐"></a>4.内存对齐</h2><p>C++ 11 引入了两个新的关键字 alignof 和 alignas 来支持对内存对齐进行控制。 alignof 关键字能够获得一个与平台相关的 std::size_t 类型的值，用于查询该平台的对齐方式。 当然我们有时候并不满足于此，甚至希望自定定义结构的对齐方式，同样，C++ 11 还引入了 alignas 来重新修饰某个结构的对齐方式。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Storage</span> &#123;</span><br><span class="line">    <span class="type">char</span>      a;</span><br><span class="line">    <span class="type">int</span>       b;</span><br><span class="line">    <span class="type">double</span>    c;</span><br><span class="line">    <span class="type">long</span> <span class="type">long</span> d;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">alignas</span>(std::<span class="type">max_align_t</span>) AlignasStorage &#123;</span><br><span class="line">    <span class="type">char</span>      a;</span><br><span class="line">    <span class="type">int</span>       b;</span><br><span class="line">    <span class="type">double</span>    c;</span><br><span class="line">    <span class="type">long</span> <span class="type">long</span> d;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    std::cout &lt;&lt; <span class="built_in">alignof</span>(Storage) &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; <span class="built_in">alignof</span>(AlignasStorage) &lt;&lt; std::endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>其中 std::max_align_t 要求每个标量类型的对齐方式严格一样，因此它几乎是最大标量没有差异， 进而大部分平台上得到的结果为 long double，因此我们这里得到的 AlignasStorage 的对齐要求是 8 或 16</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;其他杂项&quot;&gt;&lt;a href=&quot;#其他杂项&quot; class=&quot;headerlink&quot; title=&quot;其他杂项&quot;&gt;&lt;/a&gt;其他杂项&lt;/h1&gt;&lt;h2 id=&quot;1-新类型-long-long-int&quot;&gt;&lt;a href=&quot;#1-新类型-long-long-int&quot; class</summary>
      
    
    
    
    
    <category term="modern c++" scheme="https://kettycode.github.io/tags/modern-c/"/>
    
  </entry>
  
</feed>
