@@ -421,7 +421,7 @@ <h2 id="index-_1">前言</h2>
421421<blockquote>
422422<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>
423423</blockquote>
424- <p>更新时间:2025年01月02日 15:20:32 (UTC+08:00)</p>
424+ <p>更新时间:2025年01月02日 15:39:09 (UTC+08:00)</p>
425425<p><a href="https://parallel101.github.io/cppguidebook">在 GitHub Pages 浏览本书</a> | <a href="https://142857.red/book">在小彭老师自己维护的镜像上浏览本书</a></p>
426426<h2 id="index-_2">格式约定</h2>
427427<blockquote>
@@ -1505,6 +1505,7 @@ <h2 id="symbols-linkage">符号的链接类型 (linkage)</h2>
15051505<li><a href="#cpp_tricks-_8">提前返回</a></li>
15061506<li><a href="#cpp_tricks-lambda">立即调用的 Lambda</a></li>
15071507<li><a href="#cpp_tricks-lambda_1">Lambda 复用代码</a></li>
1508+ <li><a href="#cpp_tricks-if-else">打表法代替 if-else</a></li>
15081509<li><a href="#cpp_tricks-inline">类内静态成员 inline</a></li>
15091510<li><a href="#cpp_tricks-make_pair">别再 make_pair 啦!</a></li>
15101511<li><a href="#cpp_tricks-insert">insert 不会替换现有值哦</a></li>
@@ -1870,6 +1871,32 @@ <h2 id="cpp_tricks-lambda">立即调用的 Lambda</h2>
18701871 return false;
18711872}
18721873</code></pre>
1874+ <p>但有时,我们的函数可能写了额外的操作,做完查找后不想直接返回。用 <code>return</code> 提前返回的话,下面 <code>do_final</code> 部分就无法执行到,只能复读一遍。</p>
1875+ <pre><code class="language-cpp">void find(const vector<int> &v, int target) {
1876+ for (int i = 0; i < v.size(); ++i) {
1877+ if (v[i] == target) {
1878+ do_something();
1879+ do_final();
1880+ return;
1881+ }
1882+ }
1883+ do_other();
1884+ do_final();
1885+ }
1886+ </code></pre>
1887+ <p>改用 <code>goto</code> 来打断循环,又不美观了。</p>
1888+ <pre><code class="language-cpp">void find(const vector<int> &v, int target) {
1889+ for (int i = 0; i < v.size(); ++i) {
1890+ if (v[i] == target) {
1891+ do_something();
1892+ goto final;
1893+ }
1894+ }
1895+ do_other();
1896+ final:
1897+ do_final();
1898+ }
1899+ </code></pre>
18731900<p>可以包裹一个立即调用的 Lambda 块 <code>[&] { ... } ()</code>,限制提前返回的范围:</p>
18741901<pre><code class="language-cpp">void find(const vector<int> &v, int target) {
18751902 bool found = [&] {
@@ -1885,41 +1912,102 @@ <h2 id="cpp_tricks-lambda">立即调用的 Lambda</h2>
18851912 }
18861913}
18871914</code></pre>
1915+ <p>这样,return 最多只能打断到当前 Lambda 函数结束的位置,而不能打断整个大函数了。</p>
18881916<h2 id="cpp_tricks-lambda_1">Lambda 复用代码</h2>
1917+ <pre><code class="language-cpp">void calc_average() {
1918+ int res = 0;
1919+ int count = 0;
1920+ for (int i = 0; i < cat_arr.size(); i++) {
1921+ res += cat_arr[i].age;
1922+ count += cat_arr[i].count;
1923+ }
1924+ for (int i = 0; i < dog_arr.size(); i++) {
1925+ res += dog_arr[i].age;
1926+ count += dog_arr[i].count;
1927+ }
1928+ for (int i = 0; i < pig_arr.size(); i++) {
1929+ res += pig_arr[i].age;
1930+ count += pig_arr[i].count;
1931+ }
1932+ }
1933+ </code></pre>
1934+ <p>你是否被迫写出以上这种复读代码?大部分内容都是重复的,每次只有一小部分修改,导致不得不复读很多遍,非常恼人!</p>
1935+ <p>“设计模式”官腔的做法是额外定义一个函数,把重复的部分代码功能抽出来变成一个 <code>cihou</code> 模板函数,然后再 <code>calc_average</code> 里只需要调用三次这个 <code>cihou</code> 函数即可实现复用:</p>
1936+ <pre><code class="language-cpp">template <class T>
1937+ void cihou(int &res, int &count, std::vector<T> const &arr) {
1938+ for (int i = 0; i < arr.size(); i++) {
1939+ res += arr[i].age;
1940+ count += arr[i].count;
1941+ }
1942+ }
1943+
1944+ void calc_average() {
1945+ int res = 0;
1946+ int count = 0;
1947+ cihou(res, count, cat_arr);
1948+ cihou(res, count, dog_arr);
1949+ cihou(res, count, pig_arr);
1950+ }
1951+ </code></pre>
1952+ <p>然而,额外定义一个函数也太大费周章了,而且还需要把所有用到的局部变量作为参数传进去!参数部分依然需要反复复读,并且还需要一个个指定所有参数的类型,写一长串模板等。最重要的是定义外部函数会污染了全局名字空间。</p>
1953+ <blockquote>
1954+ <p><img src="../img/awesomeface.png" height="30px" width="auto" style="margin: 0; border: none"/> 洁癖程序员:脏了我的眼!</p>
1955+ </blockquote>
1956+ <p>使用 Lambda,就可以让你在 <code>calc_average</code> 当前函数里“就地解决”,无需定义外部函数。</p>
1957+ <p>更妙的是:Lambda 支持 <code>[&]</code> 语法,自动捕获所有用到的局部变量为引用!无需一个个传递局部变量引用作为函数参数,没有复读,更加无感。只有重复代码中真正区别的部分需要传参数。</p>
1958+ <pre><code class="language-cpp">void calc_average() {
1959+ int res = 0;
1960+ int count = 0;
1961+ auto cihou = [&] { // 局部 Lambda 的好处:自动帮你捕获 res 和 count!
1962+ for (int i = 0; i < arr.size(); i++) {
1963+ res += arr[i].age;
1964+ count += arr[i].count;
1965+ }
1966+ };
1967+ cihou(cat_arr);
1968+ cihou(dog_arr);
1969+ cihou(pig_arr);
1970+ }
1971+ </code></pre>
1972+ <blockquote>
1973+ <p><img src="../img/bulb.png" height="30px" width="auto" style="margin: 0; border: none"/> 现在只有两个变量 <code>res</code> 和 <code>count</code> 可能还没什么,如果重复的部分用到一大堆变量,同时还有时候用到,有时候用不到的话,你就觉得 Lambda 好用了。</p>
1974+ </blockquote>
1975+ <p>例如字符串切片函数典型的一种实现中,因为“尾巴”的伺候和“主体”的伺候,就会产生重复代码:</p>
18891976<pre><code class="language-cpp">vector<string> spilt(string str) {
18901977 vector<string> list;
18911978 string last;
18921979 for (char c: str) {
18931980 if (c == ' ') {
18941981 list.push_back(last);
1895- last.clear() ;
1982+ last = "" ;
18961983 } else {
1897- last.push_back(c) ;
1984+ last += c ;
18981985 }
18991986 }
19001987 list.push_back(last);
19011988 return list;
19021989}
19031990</code></pre>
1904- <p>上面的代码可以用 Lambda 复用:</p>
1991+ <p>上面的代码中重复的部分 <code>list.push_back(last);</code> 可以用 Lambda 复用,把重复的操作封装成局部的 Lambda :</p>
19051992<pre><code class="language-cpp">vector<string> spilt(string str) {
19061993 vector<string> list;
19071994 string last;
1908- auto push = [&] {
1995+ auto push_last = [&] {
19091996 list.push_back(last);
1910- last.clear() ;
1997+ last = "" ;
19111998 };
19121999 for (char c: str) {
19132000 if (c == ' ') {
1914- push ();
2001+ push_last ();
19152002 } else {
1916- last.push_back(c) ;
2003+ last += c ;
19172004 }
19182005 }
1919- push ();
2006+ push_last ();
19202007 return list;
19212008}
19222009</code></pre>
2010+ <h2 id="cpp_tricks-if-else">打表法代替 if-else</h2>
19232011<h2 id="cpp_tricks-inline">类内静态成员 inline</h2>
19242012<p>在头文件中定义结构体的 static 成员时:</p>
19252013<pre><code class="language-cpp">struct Class {
0 commit comments