2023:C++23 與模組時代——易用性持續打磨#
事件#
2023 年 10 月,C++23(ISO/IEC 14882:2024,俗稱 C++23)發佈。這一版延續「三年一版」節奏,以易用性與標準庫擴充為主軸。
關鍵語法/特性#
std::expected:expected<T, E>——型別安全的「值或錯誤」,取代例外與錯誤碼的二難(與 Rust 的Result<T, E>同一思想)。if consteval:判斷當前是否編譯期求值。- 多維下標運算子:
m[i, j, k]。 std::print/std::println:直接印出格式化字串(基於std::format),終結printf/cout之爭。- Ranges 擴充:
views::zip、views::chunk、views::enumerate、to<T>()。 std::mdspan、std::stacktrace、std::generator。constexpr持續擴充:constexpr更多標準庫函式。- 靜態
operator()、#elifdef、Lambda 可帶屬性**。
為何重要#
std::expected是錯誤處理的第三條路:例外(自動傳播但開銷與控制流隱晦)與錯誤碼(易被忽略)之間,「值或錯誤」進入型別系統——C++ 向 Rust 的Result、Haskell 的Either看齊。std::print終結「Hello World 的百年之爭」:std::println("Hello, {}!", name);——型別安全、無格式符漏洞(%s配錯型別的經典安全問題)。- Ranges 的
to<T>():管線運算可以直接產生容器——STL 泛型風格的最終形態。
理論與實用原因#
- 理論原因:
expected源自代數資料類型(sum type)與「錯誤即值」的函數式理論;if consteval是編譯期/執行期求值分流的語法化。 - 實用原因:Boost 與 abseil 等庫已驗證
expected十年;printf的格式符漏洞(格式字串攻擊)是資安長年痛點。
彌補了什麼缺陷#
彌補了 C++「錯誤處理只有例外與錯誤碼兩條路、輸出有格式符安全漏洞、Ranges 無法直接產生容器」的缺陷。
程式範例#
std::expected:錯誤處理的第三條路
// 舊寫法一:錯誤碼——容易被呼叫者忽略
int parse_int(const char* s, int* out); // 回傳 0/-1,沒人強制檢查
parse_int("abc", &v); // 忘了檢查回傳值,v 可能是垃圾
// 舊寫法二:例外——自動傳播但控制流隱晦、有開銷
int parse_int2(const std::string& s); // 失敗時 throw
try { v = parse_int2(s); } catch (...) { /* 呼叫端被迫包 try */ }
// C++23 新寫法:值或錯誤進入型別系統(類似 Rust 的 Result<T, E>)
#include <expected>
std::expected<int, std::string> parse_int3(const std::string& s) {
try { return std::stoi(s); }
catch (...) { return std::unexpected("not a number"); }
}
auto r = parse_int3("42");
if (r) use(*r); // 型別系統強迫你處理錯誤分支
else log(r.error());
std::println:終結 printf 的格式符漏洞
// 舊寫法:%s 配錯型別是經典資安漏洞(格式字串攻擊)
printf("Hello, %s!\n", name); // name 若非 char* 會未定義行為
printf(user_input); // 使用者輸入含 %s 直接崩潰
// C 與 C++ 的 cout 寫法:安全但囉嗦
std::cout << "Hello, " << name << "!\n";
// C++23 新寫法:型別安全、簡潔(基於 std::format)
#include <print>
std::println("Hello, {}!", name); // 編譯期檢查參數型別與個數
Ranges:views::enumerate 與 to<T>()
// 舊寫法:要 index 就得手算
std::vector<std::string> names{"a", "b", "c"};
for (std::size_t i = 0; i < names.size(); ++i)
std::cout << i << ": " << names[i] << "\n";
// C++23 新寫法:enumerate 直接給 index + 值
#include <ranges>
for (auto [i, name] : names | std::views::enumerate)
std::println("{}: {}", i, name);
// C++23 的 to<T>():管線末端直接產生容器,不必再手動建構
auto squares = std::views::iota(1, 6)
| std::views::transform([](int x) { return x * x; })
| std::ranges::to<std::vector>(); // 直接得到 vector<int>
import std;:模組取代 #include
// 舊寫法:前置處理器逐行文字替換,編譯慢、封裝差、重複解析標頭
#include <iostream>
#include <string>
#include <vector>
// C++23 新寫法:一行匯入整個標準庫,編譯速度與封裝性革命
import std;
int main() {
std::println("module era!");
}