星標(biāo)公眾號(hào),讓嵌入式知識(shí) “投喂” 不停歇!
我們第一次認(rèn)真面對現(xiàn)代 C++,不是因?yàn)橥蝗幌雽W(xué)新語法,而是項(xiàng)目把我們推到了那里。
老代碼里一堆裸指針,誰申請誰釋放已經(jīng)說不清了;新來的模塊用了 auto、lambda、std::unique_ptr,看得懂一半、心里沒底;編譯腳本里還躺著 -std=c++11,但客戶 SDK 的示例已經(jīng)開始出現(xiàn) std::optional 和 std::span。
這時(shí)候再從一本厚厚的 C++ 教程第一頁啃起,我們往往堅(jiān)持不了幾天。更實(shí)用的辦法是:先把常見特性按版本和場景拆開,知道它解決什么問題、什么時(shí)候適合用、項(xiàng)目里遇到時(shí)該去哪查。
這份現(xiàn)代 C++ 特性手冊,適合拿來做這件事。
https://github.com/AnthonyCalandra/modern-cpp-features

modern-cpp-features不是又一本 C++ 教程。

這個(gè)項(xiàng)目覆蓋 C++11 到 C++23,但它的重點(diǎn)不是把每個(gè)標(biāo)準(zhǔn)講成一門課,而是把常用特性拆成短條目。我們可以把它當(dāng)成一份工程師案頭速查表:遇到一個(gè)特性,先看說明,再看最小示例,最后回到自己的代碼里試。
項(xiàng)目按標(biāo)準(zhǔn)版本組織,每個(gè)版本單獨(dú)成文件。這個(gè)設(shè)計(jì)對嵌入式開發(fā)很友好,因?yàn)槲覀兘?jīng)常不能隨便追最新標(biāo)準(zhǔn)。芯片廠 SDK、交叉編譯器、RTOS 工具鏈、認(rèn)證要求,都會(huì)限制你能用到哪一版 C++。
大致可以這樣理解:
auto、lambda、智能指針、線程庫 | ||
std::make_unique | ||
if constexpr、std::optional、std::variant、std::string_view | ||
std::span、std::jthread、三路比較 | ||
std::expected、std::stacktrace |
如果我們只是想快速定位內(nèi)容,可以先看 項(xiàng)目概述 和 內(nèi)容結(jié)構(gòu)與組織方式。它們會(huì)告訴我們每個(gè)版本文件怎么排布,后面查起來會(huì)省很多時(shí)間。
現(xiàn)代 C++ 最大的問題不是“特性太難”,而是“特性太多”。嵌入式項(xiàng)目又有自己的約束:代碼體積、實(shí)時(shí)性、編譯器支持、團(tuán)隊(duì)習(xí)慣、歷史包袱,一個(gè)都繞不開。
所以不建議一上來就按 C++11、C++14、C++17、C++20、C++23 從頭掃。更好的方式,是先判斷自己現(xiàn)在卡在哪里。
如果項(xiàng)目風(fēng)格還停留在 C++98/03,第一批要看的不是炫技特性,而是能馬上降低 Bug 密度的東西:

auto 和 decltype,避免在復(fù)雜迭代器、模板類型上寫一串容易錯(cuò)的類型名。nullptr 替代 NULL,用 enum class 減少枚舉污染和隱式轉(zhuǎn)換。這條路線的目標(biāo)很樸素:讓代碼更不容易錯(cuò),也更容易被同事看懂。
如果團(tuán)隊(duì)已經(jīng)接受 C++11,那下一步值得把精力放在移動(dòng)語義、轉(zhuǎn)發(fā)、結(jié)構(gòu)化綁定和并發(fā)上。

這一階段不要只看語法,要多問一句:它能不能減少一次拷貝、減少一個(gè)狀態(tài)錯(cuò)誤、減少一段重復(fù)代碼?
如果正在維護(hù)的是中大型 C++ 項(xiàng)目,或者已經(jīng)在寫庫、框架、平臺(tái)層代碼,可以繼續(xù)往更高階的內(nèi)容走:


這條路線不適合趕進(jìn)度時(shí)硬上。它更像是給平臺(tái)代碼、基礎(chǔ)庫和長期演進(jìn)項(xiàng)目準(zhǔn)備的。
如果只能挑一批最容易落地的現(xiàn)代 C++ 特性,可以優(yōu)先看這些:
nullptr 和 enum class:改動(dòng)小,收益穩(wěn)定,主要提升類型安全。auto:適合復(fù)雜類型推導(dǎo),但接口和關(guān)鍵業(yè)務(wù)變量別濫用,類型信息該清楚時(shí)還是要清楚。constexpr:適合把能編譯期確定的配置、查表和計(jì)算前移,嵌入式里很有用。std::optional:適合表達(dá)“可能沒有值”,比魔法返回值和額外狀態(tài)變量更直接。std::span:適合傳遞連續(xù)內(nèi)存視圖,尤其是緩沖區(qū)、協(xié)議幀、采樣數(shù)據(jù),但要注意它不擁有數(shù)據(jù)。有些特性看起來很漂亮,落地卻要慢一點(diǎn)。比如協(xié)程、std::format、std::filesystem,在桌面或服務(wù)器上可能很自然,在 MCU、交叉編譯、裁剪標(biāo)準(zhǔn)庫的環(huán)境里就要先確認(rèn)工具鏈支持和二進(jìn)制體積。
這份手冊每個(gè)版本文件都把特性拆成語言特性和標(biāo)準(zhǔn)庫特性。查的時(shí)候,可以按下面這個(gè)順序來:
舉個(gè)例子,看到 std::unique_ptr 時(shí),不要只記住“智能指針會(huì)自動(dòng)釋放”。還要順手想清楚:這個(gè)對象有沒有唯一所有權(quán)?會(huì)不會(huì)跨線程傳遞?析構(gòu)時(shí)釋放資源是否符合硬件時(shí)序?這些才是嵌入式代碼里真正決定能不能用的地方。
第一類坑是編譯器支持。代碼里寫了 C++17,交叉編譯器不一定完整支持 C++17;頭文件能包含,也不代表庫實(shí)現(xiàn)可用。遇到編譯錯(cuò)誤,先確認(rèn) -std=c++XX、編譯器版本和標(biāo)準(zhǔn)庫實(shí)現(xiàn)。
第二類坑是“為了現(xiàn)代而現(xiàn)代”。比如一個(gè)簡單的固定數(shù)組遍歷,沒必要套一堆模板技巧;一個(gè)清晰的狀態(tài)機(jī),也不一定要改成協(xié)程?,F(xiàn)代 C++ 的價(jià)值是讓意圖更清楚、錯(cuò)誤更少、性能更可控,不是把代碼寫得更難猜。
第三類坑是忽略團(tuán)隊(duì)共識(shí)。嵌入式項(xiàng)目生命周期長,維護(hù)者可能換好幾批。引入新特性前,最好先從局部模塊開始,配合代碼評審和簡單規(guī)范,讓大家知道哪些寫法推薦,哪些寫法慎用。
如果不知道從哪里開始,可以按這個(gè)節(jié)奏走:
讀 自動(dòng)類型推導(dǎo)與關(guān)鍵字、空指針與強(qiáng)類型枚舉、范圍 for 循環(huán)。先把最常見的新語法看順眼。
讀 智能指針與資源管理、移動(dòng)語義與右值引用、Lambda 表達(dá)式基礎(chǔ)。這幾塊最容易和實(shí)際項(xiàng)目發(fā)生關(guān)系。
再根據(jù)項(xiàng)目需要去看 線程與異步編程、內(nèi)存模型與原子操作、概念與約束、協(xié)程與生成器。
現(xiàn)代 C++ 不需要一次學(xué)完。對嵌入式工程師來說,最劃算的學(xué)法是先挑那些能立刻改善項(xiàng)目質(zhì)量的特性,在真實(shí)代碼里用起來。等你能判斷“這個(gè)特性該不該放進(jìn)當(dāng)前項(xiàng)目”,才算真正學(xué)到了手。
一份打通“應(yīng)用→驅(qū)動(dòng)”的Linux底層修煉指南!
最近開始運(yùn)營視頻號(hào),歡迎大家關(guān)注: