使用者定義字面值 (C++11 起)
允許整數、浮點數、字元及字串字面值透過定義使用者定義的後綴來產生使用者定義型別的物件。
目錄 |
[編輯] 語法
使用者定義字面值是下列任一形式的表達式
| 十進位字面值 使用者定義後綴 | (1) | ||||||||
| 八進位字面值 使用者定義後綴 | (2) | ||||||||
| 十六進位字面值 使用者定義後綴 | (3) | ||||||||
| 二進位字面值 使用者定義後綴 | (4) | ||||||||
| 分數常數 指數部分 (可選的) 使用者定義後綴 | (5) | ||||||||
| 數字序列 指數部分 使用者定義後綴 | (6) | ||||||||
| 字元字面值 使用者定義後綴 | (7) | ||||||||
| 字串字面值 使用者定義後綴 | (8) | ||||||||
| 十進位字面值 | - | 與 整數字面值 中相同,一個非零的十進位數字,後接零個或多個十進位數字 |
| 八進位字面值 | - | 與 整數字面值 中相同,一個零,後接零個或多個八進位數字 |
| 十六進位字面值 | - | 與 整數字面值 中相同,0x 或 0X 後接一個或多個十六進位數字 |
| 二進位字面值 | - | 與 整數字面值 中相同,0b 或 0B 後接一個或多個二進位數字 |
| 數字序列 | - | 與 浮點字面值 中相同,一個十進位數字序列 |
| 分數常數 | - | 與 浮點字面值 中相同,要麼是 數字序列 後接一個點 (123.),要麼是可選的 數字序列 後接一個點和另一個 數字序列 (1.0 或 .12) |
| 指數部分 | - | 與 浮點字面值 中相同,字母 e 或 字母 E,後接可選的符號,再後接 數字序列 |
| 字元字面值 | - | 與 字元字面值 中相同 |
| 字串字面值 | - | 與 字串字面值 中相同,包括原始字串字面值 |
| 使用者定義後綴 | - | 一個識別字,由 字面值運算子 或 字面值運算子模板 宣告引入 (見 下文) |
| (C++14 起) |
如果一個詞彙同時符合使用者定義字面值語法和常規字面值語法,它會被假定為常規字面值 (也就是說,無法多載 123LL 中的 LL)。
當編譯器遇到帶有 使用者定義後綴 X 的使用者定義字面值時,它會執行 非限定名稱查找,尋找名稱為 operator""X 的函式。如果查找未找到宣告,則程式格式錯誤。否則,
|
a) 如果多載集包含字串字面值運算子模板,其非型別模板參數 str 是一個格式良好的模板引數,則使用者定義字面值表達式被視為函式呼叫 operator ""X<str>(); |
(自 C++20 起) |
long double operator ""_w(long double); std::string operator ""_w(const char16_t*, size_t); unsigned operator ""_w(const char*); int main() { 1.2_w; // calls operator ""_w(1.2L) u"one"_w; // calls operator ""_w(u"one", 3) 12_w; // calls operator ""_w("12") "two"_w; // error: no applicable literal operator }
當字串字面值串接發生在 轉換階段 6 時,使用者定義的字串字面值也會被串接,並且它們的 使用者定義後綴 在串接時會被忽略,除了一個後綴只能出現在所有串接的字面值上。
int main() { L"A" "B" "C"_x; // OK: same as L"ABC"_x "P"_x "Q" "R"_y; // error: two different ud-suffixes (_x and _y) }
[編輯] 字面值運算子
由使用者定義字面值呼叫的函式稱為 字面值運算子 (或者,如果它是模板,則為 字面值運算子模板)。它在命名空間範圍內宣告,就像任何其他 函式 或 函式模板 一樣 (它也可以是友元函式、函式模板的顯式實例化或特化,或者由 using 宣告引入),除了以下限制:
此函式的名稱可以有兩種形式之一
operator "" 識別字 |
(1) | (已棄用) | |||||||
operator 使用者定義字串字面值 |
(2) | ||||||||
| 識別字 | - | 用作呼叫此函式的使用者定義字面值之 使用者定義後綴 的 識別字 |
| 使用者定義字串字面值 | - | 字元序列 "",緊接著 (不帶空白字元) 構成 使用者定義後綴 的字元序列 |
使用者定義後綴 必須以下劃線 _ 開頭:不以下劃線開頭的後綴保留給標準函式庫提供的字面值運算子。它也不能包含雙下劃線 __:此類後綴也為保留用途。
如果字面值運算子是模板,它必須具有空的參數列表,並且只能有一個模板參數,該模板參數必須是元素型別為 char 的非型別模板參數包 (在這種情況下,它被稱為 數值字面值運算子模板)
template<char...> double operator ""_x();
|
或類別型別的非型別模板參數 (在這種情況下,它被稱為 字串字面值運算子模板) struct A { constexpr A(const char*); }; template<A a> A operator ""_a(); |
(自 C++20 起) |
字面值運算子只允許以下參數列表
( const char* ) |
(1) | ||||||||
( unsigned long long int ) |
(2) | ||||||||
( long double ) |
(3) | ||||||||
( char ) |
(4) | ||||||||
( wchar_t ) |
(5) | ||||||||
( char8_t ) |
(6) | (自 C++20 起) | |||||||
( char16_t ) |
(7) | ||||||||
( char32_t ) |
(8) | ||||||||
( const char*, std::size_t ) |
(9) | ||||||||
( const wchar_t*, std::size_t ) |
(10) | ||||||||
( const char8_t*, std::size_t ) |
(11) | (自 C++20 起) | |||||||
( const char16_t*, std::size_t ) |
(12) | ||||||||
( const char32_t*, std::size_t ) |
(13) | ||||||||
預設引數 不允許使用。
不允許 C 語言連結。
除了上述限制外,字面值運算子和字面值運算子模板是正常的函式 (及函式模板),它們可以被宣告為 inline 或 constexpr,它們可以具有內部或外部連結,它們可以被顯式呼叫,它們的位址可以被取得等。
#include <string> void operator ""_km(long double); // OK, will be called for 1.0_km void operator "" _km(long double); // same as above, deprecated std::string operator ""_i18n(const char*, std::size_t); // OK template<char...> double operator ""_pi(); // OK float operator ""_e(const char*); // OK // error: suffix must begin with underscore float operator ""Z(const char*); // error: all names that begin with underscore followed by uppercase // letter are reserved (NOTE: a space between "" and _). double operator"" _Z(long double); // OK. NOTE: no space between "" and _. double operator""_Z(long double); // OK: literal operators can be overloaded double operator ""_Z(const char* args); int main() {}
[編輯] 備註
自從引入使用者定義字面值以來,使用前面字串字面值後沒有空格的 固定寬度整數型別格式巨集常數 的程式碼變為無效:std::printf("%"PRId64"\n",INT64_MIN); 必須替換為 std::printf("%" PRId64"\n",INT64_MIN);。
由於 最長匹配原則,結尾為 p、P、(C++17 起) e 和 E 的使用者定義整數和浮點字面值,當其後接運算子 + 或 - 時,必須在原始碼中以空白字元或括號將其與運算子隔開
long double operator""_E(long double); long double operator""_a(long double); int operator""_p(unsigned long long); auto x = 1.0_E+2.0; // error auto y = 1.0_a+2.0; // OK auto z = 1.0_E +2.0; // OK auto q = (1.0_E)+2.0; // OK auto w = 1_p+2; // error auto u = 1_p +2; // OK
同樣適用於緊隨整數或浮點數使用者定義字面值的點運算子
#include <chrono> using namespace std::literals; auto a = 4s.count(); // Error auto b = 4s .count(); // OK auto c = (4s).count(); // OK
否則,會形成一個單一的無效預處理器數字詞彙 (例如 1.0_E+2.0 或 4s.count),這會導致編譯失敗。
| 特性測試巨集 | 數值 | 標準 | 功能 |
|---|---|---|---|
__cpp_user_defined_literals |
200809L |
(C++11) | 使用者定義字面值 |
[編輯] 關鍵字
[編輯] 範例
#include <algorithm> #include <cstddef> #include <iostream> #include <numbers> #include <string> // used as conversion from degrees (input param) to radians (returned output) constexpr long double operator""_deg_to_rad(long double deg) { long double radians = deg * std::numbers::pi_v<long double> / 180; return radians; } // used with custom type struct mytype { unsigned long long m; }; constexpr mytype operator""_mytype(unsigned long long n) { return mytype{n}; } // used for side-effects void operator""_print(const char* str) { std::cout << str << '\n'; } #if __cpp_nontype_template_args < 201911 std::string operator""_x2 (const char* str, std::size_t) { return std::string{str} + str; } #else // C++20 string literal operator template template<std::size_t N> struct DoubleString { char p[N + N - 1]{}; constexpr DoubleString(char const(&pp)[N]) { std::ranges::copy(pp, p); std::ranges::copy(pp, p + N - 1); } }; template<DoubleString A> constexpr auto operator""_x2() { return A.p; } #endif // C++20 int main() { double x_rad = 90.0_deg_to_rad; std::cout << std::fixed << x_rad << '\n'; mytype y = 123_mytype; std::cout << y.m << '\n'; 0x123ABC_print; std::cout << "abc"_x2 << '\n'; }
輸出
1.570796 123 0x123ABC abcabc
[編輯] 標準函式庫
標準函式庫中定義了以下字面值運算子
| 定義於 inline 命名空間
std::literals::complex_literals 中 | |
| 一個表示純虛數的 std::complex 字面值 (函式) | |
| 定義於內聯命名空間
std::literals::chrono_literals | |
| (C++14) |
表示小時的 std::chrono::duration 字面值 (function) |
| (C++14) |
表示分鐘的 std::chrono::duration 字面值 (function) |
| (C++14) |
表示秒的 std::chrono::duration 字面值 (function) |
| (C++14) |
表示毫秒的 std::chrono::duration 字面值 (function) |
| (C++14) |
表示微秒的 std::chrono::duration 字面值 (function) |
| (C++14) |
表示奈秒的 std::chrono::duration 字面值 (function) |
| (C++20) |
表示特定年份的 std::chrono::year 字面值 (function) |
| (C++20) |
表示某個月中的一天的 std::chrono::day 字面值 (function) |
| 定義於行內命名空間
std::literals::string_literals | |
| (C++14) |
將字元陣列字面量轉換為 basic_string(函式) |
| 定義於 inline 命名空間
std::literals::string_view_literals 中 | |
| (C++17) |
建立字元陣列字面值的字串視圖 (函式) |
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯應用於之前的 C++ 標準。
| DR | 應用於 | 出版時的行為 | 正確的行為 |
|---|---|---|---|
| CWG 1473 | C++11 | "" 與 使用者定義後綴 之間的空白字元是 在字面值運算子的宣告中是必需的 |
改為可選 |
| CWG 1479 | C++11 | 字面值運算子可以有預設引數 | 禁止 |
| CWG 2521 | C++11 | operator"" _Bq 是格式錯誤的 (沒有診斷訊息 要求),因為它使用了保留識別字 _Bq |
棄用了字面值運算子語法 在 "" 和 使用者定義後綴 之間有空白字元 |