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 语言链接。

除了上述限制之外,字面量运算符(和字面量运算符模板)是普通函数(和函数模板),它们可以声明为 inlineconstexpr ,它们可能具有内部或外部链接,它们可以显式调用,它们的地址可以被获取等。

注意

由于最大吞噬规则,以 eE (C++17 起还有 pP) 结束的用户定义整数和浮点字面量,在后随运算符 +- 时,必须在源码中以空白符或括号与运算符分隔:

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;
}
输出结果
output: 114514
output: 3.14
print value: 2022
round: 10
        9
ascii: 65
strlen: 4

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

原文链接: https://www.cnblogs.com/gyan083/p/16122633.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++ 用户定义字面量 User-defined literals

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/188634

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午2:19
下一篇 2023年2月12日 下午2:21

相关推荐