Namespaces
Variants

User-defined literals (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

目录

语法

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

十进制字面量 用户定义后缀 (1)
八进制字面量 用户定义后缀 (2)
十六进制字面量 用户定义后缀 (3)
二进制字面量 用户定义后缀 (4)
小数常量 指数部分  (可选) 用户定义后缀 (5)
数字序列 指数部分 用户定义后缀 (6)
字符字面量 用户定义后缀 (7)
字符串字面量 用户定义后缀 (8)
1-4) 用户自定义整数字面量,例如 12 _km
5-6) 用户自定义浮点数字面量,例如 0.5 _Pa
7) 用户自定义字符字面量,例如 'c' _X
8) 用户自定义字符串字面量,例如 "abd" _L u "xyz" _M
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 的函数。如果查找未找到声明,则程序非良构。否则,

1) 对于用户自定义整数字面量,
a) 若重载集包含参数类型为 unsigned long long 的字面量运算符,则用户定义字面量表达式将被视为函数调用 operator "" X ( n  ULL ) ,其中 n 为不包含 ud-suffix 的字面量;
b) 否则,重载集必须包含且仅包含以下二者之一:原始字面量运算符或数值字面量运算符模板。若重载集包含原始字面量运算符,则用户定义字面量表达式将被视为函数调用 operator "" X ( " n  ") ;
c) 否则,若重载集包含数值字面量运算符模板,则该用户定义字面量表达式被处理为函数调用 operator "" X < ' c1  ' , ' c2  ' , ' c3  ' ..., ' ck  ' > ( ) ,其中 c1 .. ck n 的各个字符,且所有字符均来自 基本字符集
2) 对于用户自定义浮点数字面量,
a) 若重载集中包含参数类型为 long double 的字面量运算符,则用户定义字面量表达式将被视为函数调用 operator "" X ( f   L ) ,其中 f 为不包含 ud-suffix 的字面量;
b) 否则,重载集必须包含且仅包含以下二者之一:原始字面量运算符或数值字面量运算符模板。若重载集包含原始字面量运算符,则用户定义字面量表达式将被视为函数调用 operator "" X ( " f   ") ;
c) 否则,若重载集包含数值字面量运算符模板,则用户定义字面量表达式被处理为函数调用 operator "" X < ' c1  ' , ' c2  ' , ' c3  ' ..., ' ck  ' > ( ) ,其中 c1 .. ck f 的各个字符,且所有字符均来自 基本字符集
3) 对于用户定义字符串字面量,令 str 表示不带 ud-suffix 的字面量:
a) 若重载集包含具有常量模板参数的字符串字面量运算符模板,且 str 是其合法模板实参,则用户定义字面量表达式将被视为函数调用 operator "" X < str > ( )
(since C++20)
b) 否则,用户定义字面量表达式将被视为函数调用 operator "" X ( str, len ) ,其中 len 是字符串字面量的长度,不包括终止空字符。
4) 对于用户定义的字符字面量,用户定义字面量表达式被视为函数调用 operator "" X ( ch ) ,其中 ch 是不带 ud-suffix 的字面量。
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 的字符序列
1) 声明一个字面量运算符。
2) 声明字面量运算符。该语法使得可以使用语言关键字和 保留标识符 作为 用户定义后缀 ,例如头文件 <complex> 中的 operator "" if

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)
1) 具有此参数列表的字面量运算符是 原始字面量运算符 ,用作整型和浮点型用户定义字面量的后备方案(参见上文)
2) 具有这些参数列表的字面量运算符是用户定义整数字面量的首选字面量运算符
3) 具有这些参数列表的字面量运算符是用户自定义浮点数字面量的首选字面量运算符
4-8) 具有这些参数列表的字面量运算符通过用户定义字符字面量调用
9-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) 用户定义字面量

关键词

operator

示例

#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
表示小时的 std::chrono::duration 字面量
(函数)
表示分钟的 std::chrono::duration 字面量
(函数)
表示秒的 std::chrono::duration 字面量
(函数)
表示毫秒的 std::chrono::duration 字面量
(函数)
表示微秒的 std::chrono::duration 字面量
(函数)
表示纳秒的 std::chrono::duration 字面量
(函数)
表示特定年份的 std::chrono::year 字面量
(函数)
表示月份中某一天的 std::chrono::day 字面量
(函数)
定义于内联命名空间 std::literals::string_literals
转换字符数组字面量为 basic_string
(函数)
定义于内联命名空间 std::literals::string_view_literals
创建字符数组字面量的字符串视图
(函数)

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 适用范围 发布时的行为 正确行为
CWG 1473 C++11 在字面值运算符声明中, "" 用户定义后缀 之间的
空格是必需的
改为可选
CWG 1479 C++11 字面值运算符可以具有默认参数 已禁止
CWG 2521 C++11 operator "" _Bq 是病式结构(不要求诊断),
因为它使用了保留标识符 _Bq
弃用了在 "" 用户定义后缀 之间
包含空格的字符值运算符语法