Skip to content

Commit 49c7a49

Browse files
committed
deploy: 1a8e72a
1 parent 52caf9a commit 49c7a49

File tree

5 files changed

+157
-17
lines changed

5 files changed

+157
-17
lines changed

Diff for: cpp_tricks/index.html

+65-9
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,6 @@
240240
<ul class="nav flex-column">
241241
</ul>
242242
</li>
243-
<li class="nav-item" data-bs-level="2"><a href="#bit-field" class="nav-link">位域(bit-field)</a>
244-
<ul class="nav flex-column">
245-
</ul>
246-
</li>
247243
<li class="nav-item" data-bs-level="2"><a href="#_3" class="nav-link">别再写构造函数啦!</a>
248244
<ul class="nav flex-column">
249245
</ul>
@@ -388,6 +384,10 @@
388384
<ul class="nav flex-column">
389385
</ul>
390386
</li>
387+
<li class="nav-item" data-bs-level="2"><a href="#ostringstream" class="nav-link">ostringstream 格式化字符串</a>
388+
<ul class="nav flex-column">
389+
</ul>
390+
</li>
391391
<li class="nav-item" data-bs-level="2"><a href="#adl" class="nav-link">ADL 机制</a>
392392
<ul class="nav flex-column">
393393
</ul>
@@ -407,6 +407,10 @@
407407
<li class="nav-item" data-bs-level="2"><a href="#this_1" class="nav-link">成员函数针对 this 的移动重载</a>
408408
<ul class="nav flex-column">
409409
</ul>
410+
</li>
411+
<li class="nav-item" data-bs-level="2"><a href="#bit-field" class="nav-link">位域(bit-field)</a>
412+
<ul class="nav flex-column">
413+
</ul>
410414
</li>
411415
</ul>
412416
</li>
@@ -422,7 +426,6 @@ <h1 id="c">应知应会 C++ 小技巧</h1>
422426
<li><a href="#_1">交换两个变量</a></li>
423427
<li><a href="#raii">RAII 地分配一段内存空间</a></li>
424428
<li><a href="#_2">读取整个文件到字符串</a></li>
425-
<li><a href="#bit-field">位域(bit-field)</a></li>
426429
<li><a href="#_3">别再写构造函数啦!</a></li>
427430
<li><a href="#_4">别再写拷贝构造函数啦!</a></li>
428431
<li><a href="#_5">提前返回</a></li>
@@ -464,11 +467,13 @@ <h1 id="c">应知应会 C++ 小技巧</h1>
464467
<li><a href="#_12">函数默认参数求值的位置是调用者</a></li>
465468
<li><a href="#_13">花括号实现安全的类型转换检查</a></li>
466469
<li><a href="#_14">临时右值转左值</a></li>
470+
<li><a href="#ostringstream">ostringstream 格式化字符串</a></li>
467471
<li><a href="#adl">ADL 机制</a></li>
468472
<li><a href="#shared_from_this">shared_from_this</a></li>
469473
<li><a href="#requires">requires 语法检测是否存在指定成员函数</a></li>
470474
<li><a href="#locale-utf8_1">设置 locale 为 .utf8 解决编码问题</a></li>
471475
<li><a href="#this_1">成员函数针对 this 的移动重载</a></li>
476+
<li><a href="#bit-field">位域(bit-field)</a></li>
472477
</ul>
473478
</li>
474479
</ul>
@@ -529,11 +534,22 @@ <h2 id="raii">RAII 地分配一段内存空间</h2>
529534
}
530535
</code></pre>
531536
<h2 id="_2">读取整个文件到字符串</h2>
532-
<pre><code class="language-cpp">TODO
533-
</code></pre>
534-
<h2 id="bit-field">位域(bit-field)</h2>
535-
<pre><code class="language-cpp">TODO
537+
<pre><code class="language-cpp">std::string file_get_content(std::string const &amp;filename) {
538+
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
539+
std::istreambuf_iterator&lt;char&gt; iit(ifs), iite;
540+
std::string content(iit, iite);
541+
return content;
542+
}
543+
544+
void file_put_content(std::string const &amp;filename, std::string const &amp;content) {
545+
std::ofstream ofs(filename, std::ios::out | std::ios::binary);
546+
ofs &lt;&lt; content;
547+
}
536548
</code></pre>
549+
<p>这样就可以把整个文件读取到内存中,进行处理后再写回文件。</p>
550+
<blockquote>
551+
<p><img src="../img/question.png" height="30px" width="auto" style="margin: 0; border: none"/><code>std::ios::binary</code> 选项打开文件,是为了避免文件中出现 <code>'\n'</code> 时,被 MSVC 标准库自动转换成 <code>'\r\n'</code>,以保证跨平台。</p>
552+
</blockquote>
537553
<h2 id="_3">别再写构造函数啦!</h2>
538554
<pre><code class="language-cpp">// C++98
539555
struct Student {
@@ -1747,11 +1763,51 @@ <h2 id="_14">临时右值转左值</h2>
17471763
<p><img src="../img/book.png" height="30px" width="auto" style="margin: 0; border: none"/> 在 Libreoffice 源码中就有应用这个帮手函数。</p>
17481764
<p><img src="../img/warning.png" height="30px" width="auto" style="margin: 0; border: none"/> 临时变量的生命周期是一行</p>
17491765
</blockquote>
1766+
<h2 id="ostringstream">ostringstream 格式化字符串</h2>
1767+
<pre><code class="language-cpp">std::string name = &quot;你好&quot;;
1768+
int answer = 42;
1769+
auto str = std::format(&quot;你好,{}!答案是 {},十六进制:0x{:02x}\n&quot;, name, answer, answer);
1770+
</code></pre>
1771+
<p>没有 C++20 之前,要么使用第三方的 <code>fmt::format</code>,要么只能使用字符串的 <code>+</code> 运算符拙劣地拼接:</p>
1772+
<pre><code class="language-cpp">auto str = std::string(&quot;你好,&quot;) + name + &quot;!答案是 &quot; + std::to_string(answer) + &quot;,十六进制:0x&quot; + std::to_string(answer) + &quot;\n&quot;;
1773+
</code></pre>
1774+
<p>这样做效率低下,且不易阅读。而且也无法实现数字按“十六进制”转字符串。</p>
1775+
<p>可以用 <code>std::ostringstream</code>,其用法与 <code>std::cout</code> 相同。只不过会把结果写入一个字符串(而不是直接输出),可以用 <code>.str()</code> 取出那个字符串。</p>
1776+
<pre><code class="language-cpp">#include &lt;sstream&gt;
1777+
1778+
std::ostringstream oss;
1779+
oss &lt;&lt; &quot;你好,&quot; &lt;&lt; name &lt;&lt; &quot;!答案是 &quot; &lt;&lt; answer &lt;&lt; &quot;,十六进制:0x&quot; &lt;&lt; std::hex &lt;&lt; std::setfill('0') &lt;&lt; std::setw(2) &lt;&lt; answer &lt;&lt; &quot;\n&quot;;
1780+
auto str = oss.str();
1781+
</code></pre>
1782+
<p>利用临时变量语法,可以浓缩写在一行里,做个 format 拙劣的模仿者:</p>
1783+
<pre><code class="language-cpp">auto str = (std::ostringstream() &lt;&lt; &quot;你好,&quot; &lt;&lt; name &lt;&lt; &quot;!答案是 &quot; &lt;&lt; answer &lt;&lt; &quot;,十六进制:0x&quot; &lt;&lt; std::hex &lt;&lt; std::setfill('0') &lt;&lt; std::setw(2) &lt;&lt; answer &lt;&lt; &quot;\n&quot;).str();
1784+
</code></pre>
17501785
<h2 id="adl">ADL 机制</h2>
1786+
<p>TODO</p>
17511787
<h2 id="shared_from_this">shared_from_this</h2>
17521788
<h2 id="requires">requires 语法检测是否存在指定成员函数</h2>
17531789
<h2 id="locale-utf8_1">设置 locale 为 .utf8 解决编码问题</h2>
17541790
<h2 id="this_1">成员函数针对 this 的移动重载</h2>
1791+
<h2 id="bit-field">位域(bit-field)</h2>
1792+
<p>在互联网编程和各种与硬盘、序列化打交道的场景中,常常需要按位拆分单个字节。</p>
1793+
<p>C 语言有专门照顾此类工作的语法糖:位域。</p>
1794+
<p>位域是一种特殊的结构体成员,可以对位进行分组,方便读取。例如,我们想要从一个字节中读取三个状态位:</p>
1795+
<pre><code class="language-cpp">struct Flag {
1796+
uint8_t a : 4; // 低 4 位
1797+
uint8_t b : 4; // 高 4 位
1798+
};
1799+
1800+
sizeof(Flag); // 1 字节大小(共 8 位)
1801+
1802+
Flag f = std::bit_cast&lt;Flag&gt;(0x21);
1803+
f.a; // 0x1
1804+
f.b; // 0x2
1805+
</code></pre>
1806+
<p>以上的代码等价于:</p>
1807+
<pre><code class="language-cpp">uint8_t f = 0x21;
1808+
int a = f &amp; 0xF; // 0x1
1809+
int b = f &gt;&gt; 4; // 0x2
1810+
</code></pre>
17551811
<!-- ## vector + unordered_map = LRU cache -->
17561812
<!-- -->
17571813
<!-- ## Lambda 捕获 unique_ptr 导致 function 报错怎么办 -->

Diff for: index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ <h2 id="_1">前言</h2>
276276
<blockquote>
277277
<p><img src="./img/bulb.png" height="30px" width="auto" style="margin: 0; border: none"/> 本书还在持续更新中……要追番的话,可以在 <a href="https://github.com/parallel101/cppguidebook">GitHub</a> 点一下右上角的 “Watch” 按钮,每当小彭老师提交新 commit,GitHub 会向你发送一封电子邮件,提醒你小彭老师更新了。</p>
278278
</blockquote>
279-
<p>更新时间:2024年08月28日 22:50:53 (UTC+08:00)</p>
279+
<p>更新时间:2024年08月28日 23:08:04 (UTC+08:00)</p>
280280
<p><a href="https://parallel101.github.io/cppguidebook">在 GitHub Pages 浏览本书</a> | <a href="https://142857.red/book">在小彭老师自己维护的镜像上浏览本书</a></p>
281281
<h2 id="_2">格式约定</h2>
282282
<blockquote>

Diff for: lambda/index.html

+16
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,22 @@ <h2 id="bind">bind 为函数对象绑定参数</h2>
11601160
<blockquote>
11611161
<p><img src="../img/warning.png" height="30px" width="auto" style="margin: 0; border: none"/> 如果不使用 <code>std::ref</code>,那么 <code>main</code> 里的局部变量 <code>x</code> 不会改变!因为 <code>std::bind</code> 有一个恼人的设计:默认按拷贝捕获,会把参数拷贝一份,而不是保留引用。</p>
11621162
</blockquote>
1163+
<p>有趣的是,placeholder 指定的参数,却不需要 <code>std::ref</code> 才能保持引用:</p>
1164+
<pre><code class="language-cpp">int inc(int &amp;x, int y) {
1165+
x += y;
1166+
}
1167+
1168+
int main() {
1169+
int x = 0;
1170+
auto inc1 = std::bind(inc, std::placeholders::_1, 1);
1171+
inc1(x); // 此处 x 是按引用传递的
1172+
fmt::println(&quot;x = {}&quot;, x); // x = 1
1173+
inc1(x);
1174+
fmt::println(&quot;x = {}&quot;, x); // x = 2
1175+
return 0;
1176+
}
1177+
</code></pre>
1178+
<p>那是因为,<code>std::placeholders::_1</code> 指定的参数会被直接完美转发给 <code>inc</code> 里的 <code>x</code>,相当于 <code>inc(x, 2);</code>。只有捕获的参数会发生拷贝,不会完美转发。</p>
11631179
<h3 id="bind_1">bind 是一个失败的设计</h3>
11641180
<p>当我们绑定出来的函数对象还需要接受参数时,就变得尤为复杂:需要使用占位符(placeholder)。</p>
11651181
<pre><code class="language-cpp">int func(int x, int y, int z, int &amp;w);

Diff for: print_page/index.html

+74-6
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ <h2 id="index-_1">前言</h2>
389389
<blockquote>
390390
<p><img src="../img/bulb.png" height="30px" width="auto" style="margin: 0; border: none"/> 本书还在持续更新中……要追番的话,可以在 <a href="https://github.com/parallel101/cppguidebook">GitHub</a> 点一下右上角的 “Watch” 按钮,每当小彭老师提交新 commit,GitHub 会向你发送一封电子邮件,提醒你小彭老师更新了。</p>
391391
</blockquote>
392-
<p>更新时间:2024年08月28日 22:50:53 (UTC+08:00)</p>
392+
<p>更新时间:2024年08月28日 23:08:04 (UTC+08:00)</p>
393393
<p><a href="https://parallel101.github.io/cppguidebook">在 GitHub Pages 浏览本书</a> | <a href="https://142857.red/book">在小彭老师自己维护的镜像上浏览本书</a></p>
394394
<h2 id="index-_2">格式约定</h2>
395395
<blockquote>
@@ -1053,7 +1053,6 @@ <h2 id="symbols-linkage">符号的链接类型 (linkage)</h2>
10531053
<li><a href="#cpp_tricks-_1">交换两个变量</a></li>
10541054
<li><a href="#cpp_tricks-raii">RAII 地分配一段内存空间</a></li>
10551055
<li><a href="#cpp_tricks-_2">读取整个文件到字符串</a></li>
1056-
<li><a href="#cpp_tricks-bit-field">位域(bit-field)</a></li>
10571056
<li><a href="#cpp_tricks-_3">别再写构造函数啦!</a></li>
10581057
<li><a href="#cpp_tricks-_4">别再写拷贝构造函数啦!</a></li>
10591058
<li><a href="#cpp_tricks-_5">提前返回</a></li>
@@ -1095,11 +1094,13 @@ <h2 id="symbols-linkage">符号的链接类型 (linkage)</h2>
10951094
<li><a href="#cpp_tricks-_12">函数默认参数求值的位置是调用者</a></li>
10961095
<li><a href="#cpp_tricks-_13">花括号实现安全的类型转换检查</a></li>
10971096
<li><a href="#cpp_tricks-_14">临时右值转左值</a></li>
1097+
<li><a href="#cpp_tricks-ostringstream">ostringstream 格式化字符串</a></li>
10981098
<li><a href="#cpp_tricks-adl">ADL 机制</a></li>
10991099
<li><a href="#cpp_tricks-shared_from_this">shared_from_this</a></li>
11001100
<li><a href="#cpp_tricks-requires">requires 语法检测是否存在指定成员函数</a></li>
11011101
<li><a href="#cpp_tricks-locale-utf8_1">设置 locale 为 .utf8 解决编码问题</a></li>
11021102
<li><a href="#cpp_tricks-this_1">成员函数针对 this 的移动重载</a></li>
1103+
<li><a href="#cpp_tricks-bit-field">位域(bit-field)</a></li>
11031104
</ul>
11041105
</li>
11051106
</ul>
@@ -1160,11 +1161,22 @@ <h2 id="cpp_tricks-raii">RAII 地分配一段内存空间</h2>
11601161
}
11611162
</code></pre>
11621163
<h2 id="cpp_tricks-_2">读取整个文件到字符串</h2>
1163-
<pre><code class="language-cpp">TODO
1164-
</code></pre>
1165-
<h2 id="cpp_tricks-bit-field">位域(bit-field)</h2>
1166-
<pre><code class="language-cpp">TODO
1164+
<pre><code class="language-cpp">std::string file_get_content(std::string const &amp;filename) {
1165+
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
1166+
std::istreambuf_iterator&lt;char&gt; iit(ifs), iite;
1167+
std::string content(iit, iite);
1168+
return content;
1169+
}
1170+
1171+
void file_put_content(std::string const &amp;filename, std::string const &amp;content) {
1172+
std::ofstream ofs(filename, std::ios::out | std::ios::binary);
1173+
ofs &lt;&lt; content;
1174+
}
11671175
</code></pre>
1176+
<p>这样就可以把整个文件读取到内存中,进行处理后再写回文件。</p>
1177+
<blockquote>
1178+
<p><img src="../img/question.png" height="30px" width="auto" style="margin: 0; border: none"/> 用 <code>std::ios::binary</code> 选项打开文件,是为了避免文件中出现 <code>'\n'</code> 时,被 MSVC 标准库自动转换成 <code>'\r\n'</code>,以保证跨平台。</p>
1179+
</blockquote>
11681180
<h2 id="cpp_tricks-_3">别再写构造函数啦!</h2>
11691181
<pre><code class="language-cpp">// C++98
11701182
struct Student {
@@ -2378,11 +2390,51 @@ <h2 id="cpp_tricks-_14">临时右值转左值</h2>
23782390
<p><img src="../img/book.png" height="30px" width="auto" style="margin: 0; border: none"/> 在 Libreoffice 源码中就有应用这个帮手函数。</p>
23792391
<p><img src="../img/warning.png" height="30px" width="auto" style="margin: 0; border: none"/> 临时变量的生命周期是一行</p>
23802392
</blockquote>
2393+
<h2 id="cpp_tricks-ostringstream">ostringstream 格式化字符串</h2>
2394+
<pre><code class="language-cpp">std::string name = &quot;你好&quot;;
2395+
int answer = 42;
2396+
auto str = std::format(&quot;你好,{}!答案是 {},十六进制:0x{:02x}\n&quot;, name, answer, answer);
2397+
</code></pre>
2398+
<p>没有 C++20 之前,要么使用第三方的 <code>fmt::format</code>,要么只能使用字符串的 <code>+</code> 运算符拙劣地拼接:</p>
2399+
<pre><code class="language-cpp">auto str = std::string(&quot;你好,&quot;) + name + &quot;!答案是 &quot; + std::to_string(answer) + &quot;,十六进制:0x&quot; + std::to_string(answer) + &quot;\n&quot;;
2400+
</code></pre>
2401+
<p>这样做效率低下,且不易阅读。而且也无法实现数字按“十六进制”转字符串。</p>
2402+
<p>可以用 <code>std::ostringstream</code>,其用法与 <code>std::cout</code> 相同。只不过会把结果写入一个字符串(而不是直接输出),可以用 <code>.str()</code> 取出那个字符串。</p>
2403+
<pre><code class="language-cpp">#include &lt;sstream&gt;
2404+
2405+
std::ostringstream oss;
2406+
oss &lt;&lt; &quot;你好,&quot; &lt;&lt; name &lt;&lt; &quot;!答案是 &quot; &lt;&lt; answer &lt;&lt; &quot;,十六进制:0x&quot; &lt;&lt; std::hex &lt;&lt; std::setfill('0') &lt;&lt; std::setw(2) &lt;&lt; answer &lt;&lt; &quot;\n&quot;;
2407+
auto str = oss.str();
2408+
</code></pre>
2409+
<p>利用临时变量语法,可以浓缩写在一行里,做个 format 拙劣的模仿者:</p>
2410+
<pre><code class="language-cpp">auto str = (std::ostringstream() &lt;&lt; &quot;你好,&quot; &lt;&lt; name &lt;&lt; &quot;!答案是 &quot; &lt;&lt; answer &lt;&lt; &quot;,十六进制:0x&quot; &lt;&lt; std::hex &lt;&lt; std::setfill('0') &lt;&lt; std::setw(2) &lt;&lt; answer &lt;&lt; &quot;\n&quot;).str();
2411+
</code></pre>
23812412
<h2 id="cpp_tricks-adl">ADL 机制</h2>
2413+
<p>TODO</p>
23822414
<h2 id="cpp_tricks-shared_from_this">shared_from_this</h2>
23832415
<h2 id="cpp_tricks-requires">requires 语法检测是否存在指定成员函数</h2>
23842416
<h2 id="cpp_tricks-locale-utf8_1">设置 locale 为 .utf8 解决编码问题</h2>
23852417
<h2 id="cpp_tricks-this_1">成员函数针对 this 的移动重载</h2>
2418+
<h2 id="cpp_tricks-bit-field">位域(bit-field)</h2>
2419+
<p>在互联网编程和各种与硬盘、序列化打交道的场景中,常常需要按位拆分单个字节。</p>
2420+
<p>C 语言有专门照顾此类工作的语法糖:位域。</p>
2421+
<p>位域是一种特殊的结构体成员,可以对位进行分组,方便读取。例如,我们想要从一个字节中读取三个状态位:</p>
2422+
<pre><code class="language-cpp">struct Flag {
2423+
uint8_t a : 4; // 低 4 位
2424+
uint8_t b : 4; // 高 4 位
2425+
};
2426+
2427+
sizeof(Flag); // 1 字节大小(共 8 位)
2428+
2429+
Flag f = std::bit_cast&lt;Flag&gt;(0x21);
2430+
f.a; // 0x1
2431+
f.b; // 0x2
2432+
</code></pre>
2433+
<p>以上的代码等价于:</p>
2434+
<pre><code class="language-cpp">uint8_t f = 0x21;
2435+
int a = f &amp; 0xF; // 0x1
2436+
int b = f &gt;&gt; 4; // 0x2
2437+
</code></pre>
23862438
<!-- ## vector + unordered_map = LRU cache -->
23872439
<!-- -->
23882440
<!-- ## Lambda 捕获 unique_ptr 导致 function 报错怎么办 -->
@@ -3304,6 +3356,22 @@ <h2 id="lambda-bind">bind 为函数对象绑定参数</h2>
33043356
<blockquote>
33053357
<p><img src="../img/warning.png" height="30px" width="auto" style="margin: 0; border: none"/> 如果不使用 <code>std::ref</code>,那么 <code>main</code> 里的局部变量 <code>x</code> 不会改变!因为 <code>std::bind</code> 有一个恼人的设计:默认按拷贝捕获,会把参数拷贝一份,而不是保留引用。</p>
33063358
</blockquote>
3359+
<p>有趣的是,placeholder 指定的参数,却不需要 <code>std::ref</code> 才能保持引用:</p>
3360+
<pre><code class="language-cpp">int inc(int &amp;x, int y) {
3361+
x += y;
3362+
}
3363+
3364+
int main() {
3365+
int x = 0;
3366+
auto inc1 = std::bind(inc, std::placeholders::_1, 1);
3367+
inc1(x); // 此处 x 是按引用传递的
3368+
fmt::println(&quot;x = {}&quot;, x); // x = 1
3369+
inc1(x);
3370+
fmt::println(&quot;x = {}&quot;, x); // x = 2
3371+
return 0;
3372+
}
3373+
</code></pre>
3374+
<p>那是因为,<code>std::placeholders::_1</code> 指定的参数会被直接完美转发给 <code>inc</code> 里的 <code>x</code>,相当于 <code>inc(x, 2);</code>。只有捕获的参数会发生拷贝,不会完美转发。</p>
33073375
<h3 id="lambda-bind_1">bind 是一个失败的设计</h3>
33083376
<p>当我们绑定出来的函数对象还需要接受参数时,就变得尤为复杂:需要使用占位符(placeholder)。</p>
33093377
<pre><code class="language-cpp">int func(int x, int y, int z, int &amp;w);

Diff for: search/search_index.json

+1-1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)