C++ 用户定义字面量 User-defined literals
百度百科:
在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。
C++ 中,字面量分为以下几种:
- 整型字面量 integer literal
- 浮点型字面量 floating-point literal
- 布尔型字面量 boolean literal
- 字符字面量 character literal
- 字符串字面量 string literal
- 用户定义字面量 user-defined literal
本文暂只对用户定义字面量进行讨论。
用户定义字面量 User-defined literal
(从 C++11 开始启用)
C++ 允许我们定义一个用户定义后缀(user-defined suffix)来把原生的整型、浮点型、字符、字符串 4 种字面量转换为我们自己需要的形式,这个形式可以是原生数据类型,也可以是自定义类。
具体地,字面量会自动调用一个函数来转换它的类型。由用户定义的字面量调用的函数称为字面量运算符(或者,如果它是模板,则称为字面量运算符模板)。
定义语法
字面量运算符的函数名以 operator""
开头,后面紧跟用户定义后缀。
返回值类型 operator"" 自定义后缀 (参数);
如果字面量运算符是一个模板,它必须有一个空的参数列表。并且只能有一个模板参数,这个模板参数必须是一个元素类型为 char 的非类型模板参数包(a non-type template parameter pack with element type char)。在这种情况下它被称为数字字面量运算符模板(numeric literal operator template)。
template <char...> 返回值类型 operator"" 自定义后缀 ();
注意,其中的自定义后缀 必须以下划线 _
开头,并符合标识符命名规范。
一些事实
其实“以下划线开头”并非是从语法上强制性的。
如果你坚持不以下划线开头,那么编译器会给你一个警告:
[Warning] literal operator suffixes not preceded by '_' are reserved for future standardization [-Wliteral-suffix]
但是毕竟只是一个“警告”。
另外,由于字面量的特殊语法结构,自己定义的这个后缀其实可以同时是 C++ 关键字而不产生冲突,这是合法的。
如 C++ 标准库 <complex>
中就定义了 if
来把整形和浮点型转换为 complex<float>
:
#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wliteral-suffix" constexpr std::complex<float> operator""if(long double __num) { return std::complex<float>{0.0F, static_cast<float>(__num)}; } constexpr std::complex<float> operator""if(unsigned long long __num) { return std::complex<float>{0.0F, static_cast<float>(__num)}; } #pragma GCC diagnostic pop
(同时使用了预编译指令来忽略刚才所说的警告)
关于参数列表:
对于字面量运算符的参数, C++ 有语法上严格的规定。参数列表仅允许以下几种类型:
Parameter Lists | Details | ||
---|---|---|---|
1 | (const char * ) |
原始字面量运算符(raw literal operators),用于整数和浮点用户定义字面量的后备方式 | |
2 | (unsigned long long int ) |
用户定义整型字面量运算符的首选方式 | |
3 | (long double ) |
用户定义浮点型字面量运算符的首选方式 | |
4 | (char ) |
通过用户定义字符型字面量调用 (user-defined character literals) |
|
5 | (wchar_t ) |
||
6 | (char8_t ) |
||
7 | (char16_t ) |
||
8 | (char32_t ) |
||
9 | (const char * , std::size_t ) |
通过用户定义字符串字面量调用 (user-defined string literals) 其中第二个参数被自动传入字符串长度 |
|
10 | (const wchar_t * , std::size_t ) |
||
11 | (const char8_t * , std::size_t ) |
||
12 | (const char16_t * , std::size_t ) |
||
13 | (const char32_t * , std::size_t ) |
||
备注 |
注意char8_t 从 C++20 开始启用。不允许使用默认参数。 不允许使用 C 语言链接。 |
除了上述限制之外,字面量运算符(和字面量运算符模板)是普通函数(和函数模板),它们可以声明为 inline
或 constexpr
,它们可能具有内部或外部链接,它们可以显式调用,它们的地址可以被获取等。
注意
由于最大吞噬规则,以 e
和 E
(C++17 起还有 p
和 P
) 结束的用户定义整数和浮点字面量,在后随运算符 +
或 -
时,必须在源码中以空白符或括号与运算符分隔:
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; // OK auto z = 1.0_E +2.0; // OK auto q = (1.0_E)+2.0; // OK auto w = 1_p+2; // 错误 auto u = 1_p +2; // OK
同样的规则适用于后随整数或浮点用户定义字面量的点运算符:
#include <chrono> using namespace std::literals; auto a = 4s.count(); // 错误 auto b = 4s .count(); // OK auto c = (4s).count(); // OK
否则会组成单个非法预处理数字记号(例如 1.0_E+2.0 或 4s.count),这导致编译失败。
最大吞噬规则
最大吞噬规则指的是编译器在处理源文件时,翻译阶段中解析预处理记号的一种特性。
如果一个给定字符前的输入已被解析为预处理记号,下一个预处理记号通常会由能构成预处理记号的最长字符序列构成,即使这样处理会导致后续分析失败。这常被称为最大吞噬。
比如经典的 i+++++i
会被解析为 i++ ++ +i
而报错,而不是人们所想的 i++ + ++i
。
示例
#include <iostream> using std::cout; namespace test { struct My_type { double value; void print() const { cout << "print value: " << value << '\n'; } }; namespace literal { // 输出 整型 和 浮点型 void operator"" _output(const char* num) { cout << "output: " << num << '\n'; } // 类型转换 inline My_type operator"" _to_My_type(unsigned long long num) { My_type tmp; tmp.value = num; return tmp; } // 四舍五入 constexpr int operator"" _round(long double num) { return int( num + 0.5 ); } // 获取字符的 ASCII 码 constexpr int operator"" _get_ascii(char c) { return int(c); } // 获取字符串长度 constexpr size_t operator"" _get_len(const char* s, size_t len) { return len; } } } // 一般来说,字面量容易重名,所以常常放在命名空间里 using namespace test::literal; int main() { 114514_output; 3.14_output; 2022_to_My_type .print(); // 注意这里的空格 cout << "round: " << 9.86_round; cout << "\n\t" << 9.39_round << '\n'; cout << "ascii: " << 'A'_get_ascii << '\n'; cout << "strlen: " << "abcd"_get_len << '\n'; return 0; }
输出结果
C++ 标准库对用户定义字面量的应用
此处仅举部分例子。
字面量运算符 | 类型 | 作用 | 起始版本 | 备注 |
---|---|---|---|---|
operator""if |
std::complex 字面量 |
表示纯虚数 | C++14 | 定义于内联命名空间std::literals::complex_literals |
operator""i |
||||
operator""il |
||||
operator""h |
std::chrono::duration 字面量 |
表示小时 | C++14 | 定义于内联命名空间std::literals::chrono_literals |
operator""min |
表示分钟 | |||
operator""s |
表示秒 | |||
operator""ms |
表示毫秒 | |||
operator""us |
表示微秒 | |||
operator""ns |
表示纳秒 | |||
operator""y |
std::chrono::year 字面量 |
表示特定年 | C++20 | |
operator""d |
std::chrono::day 字面量 |
表示月内日期 | ||
operator""s |
std::basic_string 字面量 |
转换字符数组字面量为std::basic_string |
C++14 | 定义于内联命名空间std::literals::string_literals |
operator""sv |
std::basic_string_view 字面量 |
创建一个字符数组字面量的字符串视图 | C++17 | 定义于内联命名空间std::literals::string_view_literals |
显然 std 没有遵守以下划线开头的命名规范
参考:
1.https://en.cppreference.com/w/cpp/language/user_literal
2.https://en.cppreference.com/w/cpp/language/translation_phases#.E6.9C.80.E5.A4.A7.E5.90.9E.E5.99.AC
本文来自博客园,作者:Gyan083,转载请注明原文链接:https://www.cnblogs.com/gyan083/p/16122633.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类