C++標準委員會7月科隆會議中投票經過的特性

引言

上週 C++ 標準委員會在科隆舉行了7月會議(WG21)。 會議報告請戳:《Trip Report: C++ Standards Meeting in Cologne, July 2019》html

會議中重點針對 C++20 標準已經投票經過的特性進行了若干修正,並對一些草案進行了討論和投票。相比上次會議,經過了一些新的草案。ios

移除的語言特性

新增的主要語言特性

新增的主要標準庫特性

新增特性介紹

上述特性是本人認爲的本次會議投票經過的主要特性。若要瀏覽所有特性列表,請戳引言中的鏈接。wordpress

下面我將其中一些關鍵特性做簡要介紹。函數

constinit 關鍵字

這是新引入的一個關鍵字,用於強制標記一個全局變量的初始化在編譯期完成。若初始化表達式沒法在編譯期求值,則會引起編譯錯誤。這樣即可以免全局變量的隱式運行時初始化帶來的各類各樣難以調試的 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 enum

這個特性算是對現有 using 語句用法的一個功能補全。它容許 using 語句運用在 enumenum 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::formatstd::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' 控制呈現格式

怎麼樣,是否是經過上表的說明,你們對這個格式控制有了一個初步的理解呢?下面咱們繼續展開,深刻講講 alignsigntype參數取值的具體意義。

表格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

這是一個比較實用的特性。它爲標準庫新增了一個元數據類 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;
}

概念(Concepts)採用標準庫命名規範

這個特性是指本來採用 Pascal 命名法的概念,在此次會議中投票經過改成與標準庫一致的小寫下劃線命名法。如:

std::Invocable 改成 std::invocable

std::ConvertibleTo 改成 std::convertible_to

std::EqualityComparable 改成 std::equality_comparable

等等

總結

因爲此次投票經過的特性仍是比較多的,我只挑選了幾個有表明性的展開,其餘的就再也不一一贅述。(畢竟肉翻仍是很費腦細胞的233)。感興趣的同窗能夠去看我在引言中的原文連接查閱。因爲本人水平有限,如有瑕疵,在所不免。歡迎你們拍磚指正!

相關文章
相關標籤/搜索