User-defined literals (since C++11)
Позволяет целочисленным, вещественным, символьным и строковым литералам создавать объекты пользовательского типа путем определения пользовательского суффикса.
Содержание |
Синтаксис
Пользовательский литерал — это выражение одного из следующих видов
| decimal-literal ud-suffix | (1) | ||||||||
| octal-literal ud-suffix | (2) | ||||||||
| hex-literal ud-suffix | (3) | ||||||||
| binary-literal ud-suffix | (4) | ||||||||
| fractional-constant exponent-part (необязательно) ud-suffix | (5) | ||||||||
| digit-sequence exponent-part ud-suffix | (6) | ||||||||
| character-literal ud-suffix | (7) | ||||||||
| string-literal ud-suffix | (8) | ||||||||
| 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) |
Если токен соответствует синтаксису пользовательского литерала и синтаксису обычного литерала, считается, что это обычный литерал (то есть невозможно перегрузить
LL
в
123LL
).
Когда компилятор встречает пользовательский литерал с
ud-суффиксом
X
, он выполняет
поиск по неквалифицированному имени
, ища функцию с именем
operator
""
X
. Если поиск не находит объявление, программа является некорректной. В противном случае,
|
a)
Если набор перегрузок включает шаблон литерального оператора строки с константным параметром шаблона, для которого
str
является корректным аргументом шаблона, то пользовательский литерал рассматривается как вызов функции
operator
""
X
<
str
>
(
)
;
|
(начиная с C++20) |
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-суффиксы игнорируются для целей конкатенации, за исключением того, что только один суффикс может присутствовать на всех объединённых литералах:
int main() { L"A" "B" "C"_x; // OK: то же самое, что L"ABC"_x "P"_x "Q" "R"_y; // ошибка: два разных пользовательских суффикса (_x и _y) }
Литеральные операторы
Функция, вызываемая пользовательским литералом, называется literal operator (или, если это шаблон, literal operator template ). Она объявляется так же, как любая другая function или function template в области видимости пространства имён (она также может быть функцией-другом, явным инстанцированием или специализацией шаблона функции, или введена через using-декларацию), за исключением следующих ограничений:
Название этой функции может иметь одну из двух форм:
operator ""
identifier
|
(1) | (устарело) | |||||||
operator
user-defined-string-literal
|
(2) | ||||||||
| identifier | - | идентификатор, который будет использоваться в качестве ud-suffix для пользовательских литералов, вызывающих данную функцию |
| user-defined-string-literal | - |
последовательность символов
""
с последующей (без пробела) последовательностью символов, которая становится
ud-suffix
|
ud-суффикс
должен начинаться с подчеркивания
_
: суффиксы, которые не начинаются с подчеркивания, зарезервированы для литеральных операторов, предоставляемых стандартной библиотекой. Он также не может содержать двойных подчеркиваний
__
: такие суффиксы также зарезервированы.
Если литеральный оператор является шаблоном, он должен иметь пустой список параметров и может иметь только один шаблонный параметр, который должен быть константным шаблонным параметром-пакетом с типом элементов char (в этом случае он известен как numeric literal operator template ):
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) | ||||||||
Аргументы по умолчанию не допускаются.
Связи языка C language linkage не допускаются.
Помимо указанных выше ограничений, литеральные операторы и шаблоны литеральных операторов являются обычными функциями (и шаблонами функций): они могут быть объявлены как inline или constexpr, могут иметь внутреннюю или внешнюю линковку, могут быть вызваны явно, их адреса могут быть взяты и т.д.
#include <string> void operator ""_km(long double); // OK, будет вызван для 1.0_km void operator "" _km(long double); // то же самое, устаревший вариант std::string operator ""_i18n(const char*, std::size_t); // OK template<char...> double operator ""_pi(); // OK float operator ""_e(const char*); // OK // ошибка: суффикс должен начинаться с подчеркивания float operator ""Z(const char*); // ошибка: все имена, начинающиеся с подчеркивания и следующей заглавной // буквы, зарезервированы (ПРИМЕЧАНИЕ: пробел между "" и _) double operator"" _Z(long double); // OK. ПРИМЕЧАНИЕ: нет пробела между "" и _ double operator""_Z(long double); // OK: литеральные операторы могут быть перегружены double operator ""_Z(const char* args); int main() {}
Примечания
С момента введения пользовательских литералов код, использующий макроконстанты формата для целочисленных типов фиксированной ширины без пробела после предшествующего строкового литерала, стал некорректным: std:: printf ( "%" PRId64 " \n " , INT64_MIN ) ; должен быть заменён на std:: printf ( "%" PRId64 " \n " , INT64_MIN ) ; .
Из-за
максимального поглощения
, пользовательские целочисленные и литералы с плавающей точкой, оканчивающиеся на
p
,
P
,
(since 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; // 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 ), что приводит к ошибке компиляции.
| Макроопределение для проверки функциональности | Значение | Стандарт | Функциональность |
|---|---|---|---|
__cpp_user_defined_literals
|
200809L
|
(C++11) | Пользовательские литералы |
Ключевые слова
Примеры
#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 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)
|
создает string_view из литерала массива символов
(функция) |
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 1473 | C++11 |
пробел между
""
и
ud-suffix
был
обязателен в объявлении литеральных операторов |
сделан опциональным |
| CWG 1479 | C++11 | литеральные операторы могли иметь аргументы по умолчанию | запрещено |
| CWG 2521 | C++11 |
operator
""
_Bq
был некорректным (диагностика не
требовалась), так как использует зарезервированный идентификатор
_Bq
|
устарел синтаксис литерального оператора
с пробелом между "" и ud-suffix |