上週 C++ 標準委員會在科隆舉行了7月會議(WG21)。 會議報告請戳:《Trip Report: C++ Standards Meeting in Cologne, July 2019》html
會議中重點針對 C++20 標準已經投票經過的特性進行了若干修正,並對一些草案進行了討論和投票。相比上次會議,經過了一些新的草案。ios
上述特性是本人認爲的本次會議投票經過的主要特性。若要瀏覽所有特性列表,請戳引言中的鏈接。wordpress
下面我將其中一些關鍵特性做簡要介紹。函數
這是新引入的一個關鍵字,用於強制標記一個全局變量的初始化在編譯期完成。若初始化表達式沒法在編譯期求值,則會引起編譯錯誤。這樣即可以免全局變量的隱式運行時初始化帶來的各類各樣難以調試的 BUG。優化
示例:ui
const char* g() { return "運行時初始化"; } constexpr const char* f(bool p) { return p ? "常量初始化" : g(); } constinit auto c = f(true); // OK constinit auto d = f(false); // 編譯錯誤
這個特性算是對現有 using
語句用法的一個功能補全。它容許 using 語句運用在 enum
和 enum class
之中。this
給出枚舉:spa
// china_railway.hpp enum class china_railway { cr400af, cr400bf, cr200j };
用法1:線程
using enum china_railway; constexpr auto train_type = cr400af;
用法2:指針
struct foo { // 引入枚舉成員。 using enum china_railway; }; void geek() { foo f; auto a = f.cr400af; // 使用類實例,合法 auto b = foo::cr200j; // 使用類名,合法 }
用法3:
using china_fuxing_trains = china_railway; using rubbish = china_railway::cr200j; // 枚舉值別名 constexpr auto type = china_fuxing_trains::cr400bf; constexpr auto fake_emu_train = rubbish; // 和 cr200j 等價
這是繼模塊、協程和概念後又一個重磅特性。它彌補了 C++ 標準庫缺少文本格式化支持的一個遺憾。此次經過的提案基於開源庫 fmt,語法十分優雅。文本格式化主要經過兩個新的標準庫函數 std::format
和 std::format_to
來實現。
示例1:std::format
基本用法
// 自動編號 auto text1 = std::format("{} 是動車組,但 {} 卻不是。", "CR400AF", "CR200J"); // 手動編號 auto text2 = std::format("我國領土面積 {0} 萬平方千米,人口有 {1} 億。", 960, 14); // 取消「{}」的轉義。 // text3 = "咱們的分組是:{100, 200, 300}。" auto text3 = std::format("咱們的分組是:{{{0}, {1}, {2}}}。", 100, 200, 300);
示例2:std::format
格式化控制
std::format
函數支持高級格式化控制。它使用格式控制表達式進行格式化輸出。標準格式控制表達式的形式以下所示。
"{[arg_index]:[[fill]align][sign]['#']['0'][width]['.' precision][type]}"
看似很複雜的亞子,其實不難,讓咱們經過以下的表格來解析。
說明:上式中的方括號[]
表明爲可選參數。
參數 | 取值 | 說明 |
---|---|---|
arg_id | 參數編號 | 如 0,1,2,... |
fill | 除了 '{' 和 '}' 外的任意字符 | 用於格式填充的字符 |
align | '<', '>', '=', '^' | 控制對齊方式 |
sign | '+', '-', '空格' | 控制正負號顯示方式 |
'#' | 控制數值類型輸出是否添加前綴 | |
width | 非零整數 或 '{' arg_id '}' | 控制文本長度,若不足將使用 fill 填充 |
precision | 整數 或 '{' arg_id '}' | 控制小數點後的保留的位數 |
type | 'a', 'A', 'b', 'B', 'c', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 'p', 's', 'x', 'X' | 控制呈現格式 |
怎麼樣,是否是經過上表的說明,你們對這個格式控制有了一個初步的理解呢?下面咱們繼續展開,深刻講講 align
、sign
和 type
參數取值的具體意義。
表格1: align
參數說明
align | 說明 |
---|---|
'<' | 強制左對齊。若長度不足 width,則從字符串末尾進行填充。 |
'>' | 強制右對齊。若長度不足 width,則從字符串開始進行填充。 |
'=' | 僅用於數值類型。若長度不足 width,則從 sign 或前綴以後、數字開始以前進行填充。 |
'^' | 強制居中對齊。若長度不足 width,則在字符串兩側同時進行填充。 |
表格2: sign
參數說明
sign | 說明 |
---|---|
'+' | 針對正數顯式添加 '+' 號,如 "+123"。 |
'-' | 針對負數顯式添加 '-' 號,如 "-123";針對正數不添加符號,如 "123"。這是默認選項。 |
'空格' | 針對正數在頭部添加一個空格,如 " 123";針對負數添加 '-' 號,如 "-123"。 |
表格3: type
參數說明
type | 說明 | '#' 做用 |
---|---|---|
's' | 按原始字符串輸出,僅用於字符串。 | 無 |
'c' | 按原始字符輸出,僅用於 char8_t, char16_t, char32_t, char, wchar_t 類型。 | 無 |
'b', 'B' | 按二進制輸出,僅用於整數類型,如 "1010"。 | 'b' 添加 "0b"; 'B' 添加 "0B" |
'd' | 按十進制輸出,僅用於整數類型,如 "12345"。 | 無 |
'o' | 按八進制輸出,僅用於整數類型,如 "10"。 | 添加 "0"。 |
'x', 'X' | 按十六進制輸出,僅用於整數類型。'x' 輸出小寫,'X' 輸出大寫。 | 'x' 添加 "0x";'X' 添加 "0X" |
'n' | 意義同 'd',但在其基礎上,根據本地語言設置,添加十進制的分隔符,如:"1,000,000"。 | 無 |
'a', 'A' | 按十六進制輸出,並根據 precision 保留小數,僅用於浮點類型。'a' 輸出小寫,'A' 輸出大寫。 | 無 |
'e', 'E' | 按科學計數法輸出,僅用於浮點類型。若 precision 省略,則默認爲 6。'e' 輸出小寫,'E' 輸出大寫。 | 無 |
'f', 'F' | 按十進制輸出,並根據 precision 保留小數,僅用於浮點類型。若 precision 省略則默認爲 6。'f' 輸出小寫,'F' 輸出大寫。 | 無 |
'g', 'G' | 按十進制 + 科學計數法方式輸出。若 precision 省略則默認爲 6。'g' 輸出小寫,'G' 輸出大寫。 | 無 |
'n' | 意義同 'g',但在其基礎上,根據本地語言設置,添加十進制的分隔符,如 "1,000.456"。 | 無 |
'p' | 按十六進制輸出指針值,僅用於指針類型,如 "FFFFFFFF"。 | 添加 "0x" |
寫到這,不得不感嘆,此次添加進標準的格式化函數仍是很是完備的,考慮到了各類類型的格式化。理論用於實際,讓咱們看看一些實例吧。
// 高級用法:格式化控制符 char c = 120; auto s0 = std::format("{:6}", 42); // s0 == " 42" auto s1 = std::format("{:6}", 'x'); // s1 == "x " auto s2 = std::format("{:*<6}", 'x'); // s2 == "x*****" auto s3 = std::format("{:*>6}", 'x'); // s3 == "*****x" auto s4 = std::format("{:*^6}", 'x'); // s4 == "**x***" auto s6 = std::format("{:6d}", c); // s6 == " 120" auto s7 = std::format("{:=+06d}", c); // s7 == "+00120" auto s8 = std::format("{:0=#6x}", 0xa); // s8 == "0x000a" auto s9 = std::format("{:6}", true); // s9 == "true "
示例3:std::format_to
的用法
std::format_to
函數主要用於容器類型,如 std::vector<T>
的格式化添加值。
#include <vector> #include <format> #include <string> int main() { std::vector<std::string> data; for (size_t i = 0; i < 100; i++) { std::format_to(std::back_inserter(data), "個人工號是:{} ", i); } }
其中格式字符串的用法等同於 std::format
函數,可參考上述有關內容。
這是一個比較實用的特性。它爲標準庫新增了一個元數據類 std::source_location
,可爲用戶提供當前代碼的上下文信息。該類的定義以下所示。
namespace std { struct source_location { constexpr source_location() noexcept; constexpr uint_least32_t line() const noexcept; constexpr uint_least32_t column() const noexcept; constexpr const char* file_name() const noexcept; constexpr const char* function_name() const noexcept; static consteval source_location current() noexcept; }; }
示例:
#include <source_location> #include <format> #include <iostream> void foo() { // Get the location of this line. auto loc = std::source_location::current(); std::cout << std::format("Line: {}, Column: {}, Function: {}, File: {}", loc.line(), loc.column(), loc.function_name(), loc.file_name()) << std::endl; }
這個特性是指本來採用 Pascal 命名法的概念,在此次會議中投票經過改成與標準庫一致的小寫下劃線命名法。如:
std::Invocable
改成 std::invocable
std::ConvertibleTo
改成 std::convertible_to
std::EqualityComparable
改成 std::equality_comparable
等等
因爲此次投票經過的特性仍是比較多的,我只挑選了幾個有表明性的展開,其餘的就再也不一一贅述。(畢竟肉翻仍是很費腦細胞的233)。感興趣的同窗能夠去看我在引言中的原文連接查閱。因爲本人水平有限,如有瑕疵,在所不免。歡迎你們拍磚指正!