用户自定义字面量 (自 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),这会导致编译失败。
特性测试宏 | 值 | Std | 特性 |
---|---|---|---|
__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
[编辑] 标准库
以下字面量运算符在标准库中定义
定义于内联命名空间
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 |
废弃了字面量运算符语法 在 "" 和 ud-后缀 之间存在空格 的情况 |