名稱空間
變體
操作

使用者定義的字面量 (C++11 起)

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
通用主題
流程控制
條件執行語句
if
迭代語句(迴圈)
for
範圍-for (C++11)
跳轉語句
函式
函式宣告
Lambda 函式表示式
inline 說明符
動態異常規範 (直到 C++17*)
noexcept 說明符 (C++11)
異常
名稱空間
型別
說明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
儲存期說明符
初始化
表示式
替代表示
字面量
布林 - 整數 - 浮點
字元 - 字串 - nullptr (C++11)
使用者定義 (C++11)
工具
屬性 (C++11)
型別
typedef 宣告
類型別名宣告 (C++11)
型別轉換
記憶體分配
類特有的函式屬性
explicit (C++11)
static

特殊成員函式
模板
雜項
 
 

允許整數、浮點、字元和字串字面量透過定義使用者定義的字尾來生成使用者定義型別的物件。

目錄

[編輯] 語法

使用者定義字面量是以下任意形式的表示式

十進位制-字面量 ud-字尾 (1)
八進位制-字面量 ud-字尾 (2)
十六進位制-字面量 ud-字尾 (3)
二進位制-字面量 ud-字尾 (4)
小數-常量 指數-部分 (可選) ud-字尾 (5)
數字-序列 指數-部分 ud-字尾 (6)
字元-字面量 ud-字尾 (7)
字串-字面量 ud-字尾 (8)
1-4) 使用者定義整數型字面量,例如 12_km
5-6) 使用者定義浮點型字面量,例如 0.5_Pa
7) 使用者定義字元型字面量,例如 'c'_X
8) 使用者定義字串型字面量,例如 "abd"_Lu"xyz"_M
十進位制-字面量 - 整數型字面量中相同,一個非零十進位制數字後跟零個或多個十進位制數字
八進位制-字面量 - 整數型字面量中相同,一個零後跟零個或多個八進位制數字
十六進位制-字面量 - 整數型字面量中相同,0x0X 後跟一個或多個十六進位制數字
二進位制-字面量 - 整數型字面量中相同,0b0B 後跟一個或多個二進位制數字
數字-序列 - 浮點型字面量中相同,一系列十進位制數字
小數-常量 - 浮點型字面量中相同,一個數字-序列後跟一個點(123.),或者一個可選的數字-序列後跟一個點和另一個數字-序列1.0.12
指數-部分 - 浮點型字面量中相同,字母e或字母E後跟可選符號,再後跟數字-序列
字元-字面量 - 字元型字面量相同
字串-字面量 - 字串型字面量相同,包括原始字串字面量
ud-字尾 - 一個識別符號,由“字面量運算子”或“字面量運算子模板”宣告引入(參見下方

整數浮點數字序列中,允許在任意兩個數字之間使用可選的分隔符 '

(C++14 起)

如果一個標記同時匹配使用者定義的字面量語法和常規字面量語法,則它被假定為常規字面量(即,無法在 123LL 中過載 LL)。

當編譯器遇到帶有 ud-字尾 X 的使用者定義字面量時,它會執行非限定名稱查詢,尋找名為 operator""X 的函式。如果查詢沒有找到宣告,程式將是病態的。否則,

1) 對於使用者定義整數型字面量,
a) 如果過載集包含一個引數型別為 unsigned long long 的字面量運算子,則使用者定義的字面量表達式被視為函式呼叫 operator ""X(n ULL),其中 n 是不帶 ud-字尾 的字面量;
b) 否則,過載集必須包含一個原始字面量運算子或一個數字字面量運算子模板,但不能兩者都包含。如果過載集包含一個原始字面量運算子,則使用者定義的字面量表達式被視為函式呼叫 operator""X("n ")
c) 否則,如果過載集包含一個數字字面量運算子模板,則使用者定義的字面量表達式被視為函式呼叫 operator""X<'c1 ', 'c2 ', 'c3 '..., 'ck '>(),其中 c1..ckn 的各個字元,並且所有字元都來自基本字元集
2) 對於使用者定義浮點型字面量,
a) 如果過載集包含一個引數型別為 long double 的字面量運算子,則使用者定義的字面量表達式被視為函式呼叫 operator ""X(f  L),其中 f 是不帶 ud-字尾 的字面量;
b) 否則,過載集必須包含一個原始字面量運算子或一個數字字面量運算子模板,但不能兩者都包含。如果過載集包含一個原始字面量運算子,則使用者定義的字面量表達式被視為函式呼叫 operator ""X("f  ")
c) 否則,如果過載集包含一個數字字面量運算子模板,則使用者定義的字面量表達式被視為函式呼叫 operator""X<'c1 ', 'c2 ', 'c3 '..., 'ck '>(),其中 c1..ckf 的各個字元,並且所有字元都來自基本字元集
3) 對於使用者定義字串型字面量,設 str 是不帶 ud-字尾 的字面量
a) 如果過載集包含一個字串字面量運算子模板,其具有一個非型別模板引數,且 str 是一個格式良好的模板實參,則使用者定義的字面量表達式被視為函式呼叫 operator ""X<str>()
(C++20 起)
b) 否則,使用者定義的字面量表達式被視為函式呼叫 operator ""X (str, len),其中 len 是字串字面量的長度,不包括終止空字元。
4) 對於使用者定義字元型字面量,使用者定義的字面量表達式被視為函式呼叫 operator ""X(ch),其中 ch 是不帶 ud-字尾 的字面量。
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 中進行連線時,使用者定義字串字面量也會被連線,並且它們的 ud-字尾 在連線時會被忽略,除非所有連線的字面量只出現一個字尾。

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)
識別符號 - 用作呼叫此函式的使用者定義字面量的 ud-字尾識別符號
使用者定義字串字面量 - 字元序列 "",緊隨其後(無空格)是成為 ud-字尾 的字元序列
1) 宣告一個字面量運算子。
2) 宣告一個字面量運算子。此語法使得可以使用語言關鍵字和保留識別符號作為 ud-字尾,例如,來自標頭檔案 <complex>operator ""if

ud-字尾 必須以下劃線 _ 開頭:不以下劃線開頭的字尾保留給標準庫提供的字面量運算子。它也不能包含雙下劃線 __:此類字尾也保留。

如果字面量運算子是模板,它必須有一個空的引數列表,並且只能有一個模板引數,該引數必須是一個非型別模板引數包,其元素型別為 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)
1) 具有此引數列表的字面量運算子是原始字面量運算子,用作整數和浮點使用者定義字面量的回退(見上文)
2) 具有此引數列表的字面量運算子是使用者定義整數型字面量的首選字面量運算子
3) 具有此引數列表的字面量運算子是使用者定義浮點型字面量的首選字面量運算子
4-8) 使用者定義字元型字面量呼叫具有此引數列表的字面量運算子
9-13) 使用者定義字串型字面量呼叫具有此引數列表的字面量運算子

預設實參不允許。

C 語言連結不允許。

除了上述限制外,字面量運算子和字面量運算子模板是普通的函式(和函式模板),它們可以宣告為內聯或 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);

由於最長匹配原則,以 pP(C++17 起) eE 結尾的使用者定義整數和浮點字面量,當後面跟著運算子 +- 時,必須在原始碼中用空格或括號將它們與運算子分開。

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.04s.count),導致編譯失敗。

功能測試宏 標準 特性
__cpp_user_defined_literals 200809L (C++11) 使用者定義的字面量

[編輯] 關鍵詞

operator

[編輯] 示例

#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

[編輯] 標準庫

標準庫中定義了以下字面量運算子

在內聯名稱空間 std::literals::complex_literals 中定義
表示純虛數的 std::complex 字面量
(函式) [編輯]
在內聯名稱空間 std::literals::chrono_literals 中定義
(C++14)
表示小時的 std::chrono::duration 字面量
(函式) [編輯]
(C++14)
表示分鐘的 std::chrono::duration 字面量
(函式) [編輯]
(C++14)
表示秒的 std::chrono::duration 字面量
(函式) [編輯]
(C++14)
表示毫秒的 std::chrono::duration 字面量
(函式) [編輯]
(C++14)
表示微秒的 std::chrono::duration 字面量
(函式) [編輯]
(C++14)
表示納秒的 std::chrono::duration 字面量
(函式) [編輯]
(C++20)
表示特定年份的 std::chrono::year 字面量
(函式) [編輯]
(C++20)
表示月份中某天的 std::chrono::day 字面量
(函式) [編輯]
在內聯名稱空間 std::literals::string_literals 中定義
(C++14)
將字元陣列字面量轉換為 basic_string
(函式) [編輯]
在內聯名稱空間 std::literals::string_view_literals 中定義
(C++17)
建立字元陣列字面量的字串檢視
(函式) [編輯]

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 1473 C++11 在字面量運算子宣告中,""ud-字尾 之間的空格
被要求
變為可選
CWG 1479 C++11 字面量運算子可以有預設實參 已禁止
CWG 2521 C++11 operator"" _Bq 是非良構的(不需要診斷)
因為它使用了保留識別符號 _Bq
廢棄了字面量運算子語法
""ud-字尾 之間帶有空格