用戶定義字面量
用戶定義字面量(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]