User-defined literals (since C++11)
允许通过定义用户定义后缀,使整型、浮点型、字符型和字符串字面量能够生成用户自定义类型的对象。
目录 |
语法
用户定义字面量是以下任意形式的表达式
| 十进制字面量 用户定义后缀 | (1) | ||||||||
| 八进制字面量 用户定义后缀 | (2) | ||||||||
| 十六进制字面量 用户定义后缀 | (3) | ||||||||
| 二进制字面量 用户定义后缀 | (4) | ||||||||
| 小数常量 指数部分 (可选) 用户定义后缀 | (5) | ||||||||
| 数字序列 指数部分 用户定义后缀 | (6) | ||||||||
| 字符字面量 用户定义后缀 | (7) | ||||||||
| 字符串字面量 用户定义后缀 | (8) | ||||||||
| decimal-literal | - | 与 整数字面值 中相同,一个非零十进制数字后跟零个或多个十进制数字 |
| octal-literal | - | 与 整数字面值 中相同,一个零后跟零个或多个八进制数字 |
| hex-literal | - |
与
整数字面值
中相同,
0x
或
0X
后跟一个或多个十六进制数字
|
| binary-literal | - |
与
整数字面值
中相同,
0b
或
0B
后跟一个或多个二进制数字
|
| digit-sequence | - | 与 浮点数字面值 中相同,一个十进制数字序列 |
| fractional-constant | - | 与 浮点数字面值 中相同,可以是一个 digit-sequence 后跟点号( 123 . ),或可选的 digit-sequence 后跟点号和另一个 digit-sequence ( 1.0 或 .12 ) |
| exponent-part | - |
与
浮点数字面值
中相同,字母
e
或字母
E
后跟可选符号,再后跟
digit-sequence
|
| character-literal | - | 与 字符字面值 中相同 |
| string-literal | - | 与 字符串字面值 中相同,包括原始字符串字面值 |
| ud-suffix | - | 一个标识符,由 字面值运算符 或 字面值运算符模板 声明引入(参见 下文 ) |
| (since C++14) |
如果一个记号同时匹配用户定义字面量语法和常规字面量语法,则假定其为常规字面量(即无法重载
123LL
中的
LL
)。
当编译器遇到带有
ud-suffix
X
的用户定义字面量时,它会执行
非限定名称查找
,寻找名为
operator
""
X
的函数。如果查找未找到声明,则程序非良构。否则,
|
a)
若重载集包含具有常量模板参数的字符串字面量运算符模板,且
str
是其合法模板实参,则用户定义字面量表达式将被视为函数调用
operator
""
X
<
str
>
(
)
;
|
(since 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; // 调用 operator ""_w(1.2L) u"one"_w; // 调用 operator ""_w(u"one", 3) 12_w; // 调用 operator ""_w("12") "two"_w; // 错误:没有适用的字面量运算符 }
当 翻译阶段6 发生字符串字面量连接时,用户定义字符串字面量也会被连接,且其 ud-suffix 在连接过程中会被忽略,但所有连接的字面量中只能出现一个后缀:
int main() { L"A" "B" "C"_x; // 正确:等同于 L"ABC"_x "P"_x "Q" "R"_y; // 错误:存在两个不同的用户定义后缀(_x 和 _y) }
字面量运算符
由用户定义字面量调用的函数被称为 字面量运算符 (若为模板则称为 字面量运算符模板 )。其声明方式与任何其他 函数 或 函数模板 在命名空间作用域中的声明相同(也可以是友元函数、函数模板的显式实例化或特化,或通过using声明引入),但存在以下限制:
此函数的名称可具有以下两种形式之一:
operator ""
标识符
|
(1) | (已弃用) | |||||||
operator
用户定义字符串字面量
|
(2) | ||||||||
| identifier | - | 用作 ud-suffix 的 标识符 ,该后缀将用于调用此函数的用户定义字面量 |
| user-defined-string-literal | - |
字符序列
""
后紧跟(无空格)将作为
ud-suffix
的字符序列
|
ud-后缀
必须以下划线
_
开头:不以以下划线开头的后缀是为标准库提供的字面量运算符保留的。它也不能包含双下划线
__
:此类后缀同样被保留。
如果字面量运算符是模板,则必须具有空参数列表且只能拥有一个模板参数,该参数必须是元素类型为 char 的常量模板参数包(这种情况下它被称为 数值字面量运算符模板 ):
template<char...> double operator ""_x();
|
或类类型的常量模板参数(此时称为 字符串字面量运算符模板 ): struct A { constexpr A(const char*); }; template<A a> A operator ""_a(); |
(since 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 language linkage 是不允许的。
除上述限制外,字面量运算符和字面量运算符模板是普通函数(及函数模板),可声明为 inline 或 constexpr,可具有内部或外部链接,可被显式调用,可获取其地址等。
#include <string> void operator ""_km(long double); // 正确,将用于处理 1.0_km void operator "" _km(long double); // 同上,但已弃用 std::string operator ""_i18n(const char*, std::size_t); // 正确 template<char...> double operator ""_pi(); // 正确 float operator ""_e(const char*); // 正确 // 错误:后缀必须以下划线开头 float operator ""Z(const char*); // 错误:所有以下划线后接大写字母开头的名称均为保留名称(注意:"" 和 _ 之间有空格) double operator"" _Z(long double); // 正确。注意:"" 和 _ 之间无空格 double operator""_Z(long double); // 正确:字面量运算符可以重载 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; // 错误 auto y = 1.0_a+2.0; // 正确 auto z = 1.0_E +2.0; // 正确 auto q = (1.0_E)+2.0; // 正确 auto w = 1_p+2; // 错误 auto u = 1_p +2; // 正确
同样适用于整数或浮点数用户定义字面量后的点运算符:
#include <chrono> using namespace std::literals; auto a = 4s.count(); // 错误 auto b = 4s .count(); // 正确 auto c = (4s).count(); // 正确
否则,会形成单个无效的预处理数字标记(例如 1.0 _E + 2.0 或 4s. count ),这将导致编译失败。
| 功能测试宏 | 值 | 标准 | 功能 |
|---|---|---|---|
__cpp_user_defined_literals
|
200809L
|
(C++11) | 用户定义字面量 |
关键词
示例
#include <algorithm> #include <cstddef> #include <iostream> #include <numbers> #include <string> // 用于将度数(输入参数)转换为弧度(返回输出) constexpr long double operator""_deg_to_rad(long double deg) { long double radians = deg * std::numbers::pi_v<long double> / 180; return radians; } // 用于自定义类型 struct mytype { unsigned long long m; }; constexpr mytype operator""_mytype(unsigned long long n) { return mytype{n}; } // 用于产生副作用 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 字符串字面量运算符模板 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++ 标准。
| DR | 适用范围 | 发布时的行为 | 正确行为 |
|---|---|---|---|
| CWG 1473 | C++11 |
在字面值运算符声明中,
""
与
用户定义后缀
之间的
空格是必需的 |
改为可选 |
| CWG 1479 | C++11 | 字面值运算符可以具有默认参数 | 已禁止 |
| CWG 2521 | C++11 |
operator
""
_Bq
是病式结构(不要求诊断),
因为它使用了保留标识符
_Bq
|
弃用了在
""
与
用户定义后缀
之间
包含空格的字符值运算符语法 |