用户定义字面量
用户定义字面量(user-defined literal)是C++程序设计语言从C++11标准开始支持的用户定义类型的字面量。
背景
传统的C/C++提供了多种字面量。例如,“12.5
”是一个字面量,被编译器当作double
类型且值为12.5。如果增加后缀“f
”,则“12.5f
”就是float
类型且值为12.5。字面量的后缀是由C/C++标准规定的,程序不能增加新的字面量类型或其后缀。
C++11增加了用户自行定义新的字面量后缀并由此用字面量构造对象的能力。这是通过定义字面量运算符(literal operator)函数或函数模板实现。该运算符名字由一对相邻双引号前导。[1]字面量运算符通常在用户定义字面量的地方被隐式调用。例如,
#include<iostream>
struct S{
int value;
};
S operator ""_mysuffix(unsigned long long v) //用户定义字面量运算符的实现
{
S s_;
s_.value=(int)v;
return s_;
}
int main()
{
S sv;
sv=101_mysuffix; //这里的101是类型S的字面量
std::cout<<sv.value<<std::endl;
return 0;
}
简介
用户定义字面量分为四类:[2]
- 数值型字面量
- 整数型字面量
- 浮点型字面量
- 字符串字面量
- 字符字面量
编译器对源程序做词法分析时自动判决当前的用户定义字面量属于哪一类,然后根据字面量后缀标识符,隐式调用相应的字面量运算符函数或模板函数,建构出相应类型的对象实例。
用户字面量运算符的声明、定义,可以放在名字空间(namespace)中以避免名字的冲突。
编译器做字面量变换分为两个阶段:原始(raw)与加工后(cooked)。原始字面量是特定类型的字符的序列。加工后字面量是单独的类型。例如:C++字面量1234
,作为原始字面量是字符序列'1'
, '2'
, '3'
, '4'
;作为加工后字面量是整数1234。C++字面量0xA
的原始形式是'0'
, 'x'
, 'A'
,加工后形式为整数10。
字面量既可以用原始形式也可以用加工后形式扩展。但字符串字面量只能以加工后形式处理,因为字符串可以有前缀影响了字符的意义与类型。例如前缀为‘L’,表示宽字符。
所有用户定义字面量只能使用后缀。以下划线符号(_
) 以外的任何字符开始的字面量后缀都被C++标准保留。因此,所有用户定义的字面量都应该以下划线符号(_
) 为后缀开始字符。
原始形式字面量
用户定义字面量的原始形式示例如下:
OutputType operator "" _suffix(const char * literal_string); //声明用户定义字面量的处理函数
OutputType some_variable = 1234_suffix; //使用用户定义字面量
原始字面量运算符(raw literal operator)只能有单个参数,参数类型为const char *
。[3]
处理整型或浮点型的用户定义原始形式字面量可以选择使用可变参数模板:
template<char...> OutputType operator "" _tuffix(); //声明函数模板
OutputType some_variable = 1234_tuffix; //使用
OutputType another_variable = 2.17_tuffix;//使用
字面量运算符模板(literal operator template)的函数形参表必须为空;模板形参表只能有一个成员,即非类型的模板参数包(non-type template paremeter pack),其成员类型为char
。[4]
例如,上例中的字面量处理运算符函数模板被实例化为operator "" _tuffix<'1', '2', '3', '4'>()
。这种情形下,没有字符串终止的null字符。这种形式的目的是使用C++11新增加的constexpr
关键字,允许字面量在编译期被正确处理,这必须满足OutputType
类型是常量表达式可构造(constexpr-constructable)与可复制(copyable)的,且字面量运算符函数(模板)是constexpr
的。
例如:
#include<iostream>
#include<string>
using namespace std;
struct S{
S (const char * lls): value(lls){};
string value;
};
template < char ... cdots> S operator "" _mysuffix()
{
const char cv[] {cdots...,'\0'}; //把可变的模板参数用于初始化器(initializer)
S sv_(cv);
return sv_;
}
int main()
{
S sv {1234.567_mysuffix} ;
std::cout<<sv.value<<std::endl;
return 0;
}
加工后形式的用户定义字面量
加工后形式的数值字面量所采用的类型,对于整型字面量是unsigned long long
,对于浮点型字面量是long double
。
不需要有符号的整型,因为整型字面量的符号前缀被分析(parse)为一个包含了作为酉前缀运算符(unary prefix operator)的表达式。
例如:
OutputType operator "" _suffix(unsigned long long);
OutputType operator "" _suffix(long double);
OutputType some_variable = 1234_suffix; // Uses the 'unsigned long long' overload.
OutputType another_variable = 3.1416_suffix; // Uses the 'long double' overload.
字符串字面量
对于字符串字面量,可使用下述形式,并可配合C++11的几种字符串前缀:
OutputType operator "" _ssuffix(const char * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const wchar_t * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char16_t * string_values, size_t num_chars);
OutputType operator "" _ssuffix(const char32_t * string_values, size_t num_chars);
OutputType some_variable = "1234"_ssuffix; // Uses the 'const char *' overload.
OutputType some_variable = u8"1234"_ssuffix; // Uses the 'const char *' overload.
OutputType some_variable = L"1234"_ssuffix; // Uses the 'const wchar_t *' overload.
OutputType some_variable = u"1234"_ssuffix; // Uses the 'const char16_t *' overload.
OutputType some_variable = U"1234"_ssuffix; // Uses the 'const char32_t *' overload.
上述字符串字面量运算符函数的第二个参数表示字符串不包括尾null字符的长度。字符串字面量没有可选的模板函数实现。
字符字面量
字符字面量可类似于字符串字面量那样定义与使用。 例如:
#include<iostream>
#include<string>
using namespace std;
struct S{
S (const char * lls): value(lls){};
string value;
};
S operator "" _mysuffix(const char * string_values, size_t num_chars) //字符串字面量
{
S sv_ (string_values);
return sv_;
}
S operator "" _mysuffix(char value) //字符字面量
{
const char cv[] {value,'\0'};
S sv_ (cv);
return sv_;
}
int main()
{
S sv {"hello"_mysuffix} ;
std::cout<<sv.value<<std::endl;
S cv {'h'_mysuffix} ;
std::cout<<cv.value<<std::endl;
return 0;
}
C++标准中的几个用户定义字面量
C++11引入了用户定义字面量后缀的语法,但标准库没有使用任何此类东西。C++14标准增加了下述的字面量后缀:
- "s",用于结合字符串前缀创建各类
std::basic_string
字面量; - "h", "min", "s", "ms", "us", "ns", 用于创建对应的
std::chrono::duration
时间间隔。例如:
string str = "hello world"s;
chrono::duration dur = 60s;
例子中的两个"s"作为字面量后缀并不一样,因为前者用于字符串字面量,后者用于数值字面量。[5]