命名空间
变体
操作

用户定义的字面量 (C++11 起)

来自 cppreference.cn
< 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)
存储期说明符
初始化
 
 

允许整数、浮点、字符和字符串字面量通过定义用户定义的后缀来生成用户定义类型的对象。

目录

[编辑] 语法

用户定义字面量是以下任意形式的表达式

十进制-字面量 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-后缀 之间带有空格