User-defined literals (since C++11)
Permite que los literales enteros, de punto flotante, de caracteres y de cadena produzcan objetos de tipo definido por el usuario mediante la definición de un sufijo definido por el usuario.
Contenidos |
Sintaxis
Un literal definido por el usuario es una expresión de cualquiera de las siguientes formas
| decimal-literal ud-suffix | (1) | ||||||||
| octal-literal ud-suffix | (2) | ||||||||
| hex-literal ud-suffix | (3) | ||||||||
| binary-literal ud-suffix | (4) | ||||||||
| fractional-constant exponent-part (opcional) ud-suffix | (5) | ||||||||
| digit-sequence exponent-part ud-suffix | (6) | ||||||||
| character-literal ud-suffix | (7) | ||||||||
| string-literal ud-suffix | (8) | ||||||||
| decimal-literal | - | igual que en literal entero , un dígito decimal distinto de cero seguido de cero o más dígitos decimales |
| octal-literal | - | igual que en literal entero , un cero seguido de cero o más dígitos octales |
| hex-literal | - |
igual que en
literal entero
,
0x
o
0X
seguido de uno o más dígitos hexadecimales
|
| binary-literal | - |
igual que en
literal entero
,
0b
o
0B
seguido de uno o más dígitos binarios
|
| digit-sequence | - | igual que en literal de punto flotante , una secuencia de dígitos decimales |
| fractional-constant | - | igual que en literal de punto flotante , ya sea una digit-sequence seguida de un punto ( 123 . ) o una digit-sequence opcional seguida de un punto y otra digit-sequence ( 1.0 o .12 ) |
| exponent-part | - |
igual que en
literal de punto flotante
, la letra
e
o la letra
E
seguida de signo opcional, seguida de
digit-sequence
|
| character-literal | - | igual que en literal de carácter |
| string-literal | - | igual que en literal de cadena , incluyendo literales de cadena sin formato |
| ud-suffix | - | un identificador, introducido por una declaración de operador literal o plantilla de operador literal (ver abajo ) |
|
En las secuencias de dígitos de
literales enteros
y
literales de punto flotante
, se permiten separadores opcionales
|
(desde C++14) |
Si un token coincide con la sintaxis de literal definido por el usuario y con la sintaxis de literal regular, se asume que es un literal regular (es decir, es imposible sobrecargar
LL
en
123LL
).
Cuando el compilador encuentra un literal definido por el usuario con el
ud-suffix
X
, realiza una
búsqueda de nombre no calificada
, buscando una función con el nombre
operator
""
X
. Si la búsqueda no encuentra una declaración, el programa está mal formado. De lo contrario,
|
a)
Si el conjunto de sobrecarga incluye una plantilla de operador de cadena literal con un parámetro de plantilla constante para el cual
str
es un argumento de plantilla válido, entonces la expresión de literal definida por el usuario se trata como una llamada de función
operator
""
X
<
str
>
(
)
;
|
(desde 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; // llama a operator ""_w(1.2L) u"one"_w; // llama a operator ""_w(u"one", 3) 12_w; // llama a operator ""_w("12") "two"_w; // error: no hay un operador literal aplicable }
Cuando la concatenación de literales de cadena tiene lugar en la fase de traducción 6 , los literales de cadena definidos por el usuario también se concatenan, y sus ud-suffix se ignoran para el propósito de concatenación, excepto que solo un sufijo puede aparecer en todos los literales concatenados:
int main() { L"A" "B" "C"_x; // OK: igual que L"ABC"_x "P"_x "Q" "R"_y; // error: dos sufijos de usuario diferentes (_x y _y) }
Operadores literales
La función llamada por un literal definido por el usuario se conoce como literal operator (o, si es una plantilla, literal operator template ). Se declara como cualquier otra function o function template en el ámbito del espacio de nombres (también puede ser una función friend, una instanciación explícita o especialización de una plantilla de función, o introducida mediante una using-declaration), con excepción de las siguientes restricciones:
El nombre de esta función puede tener una de las dos formas:
operator ""
identificador
|
(1) | (obsoleto) | |||||||
operator
literal-de-cadena-definido-por-el-usuario
|
(2) | ||||||||
| identifier | - | el identificador a utilizar como sufijo-ud para los literales definidos por el usuario que llamarán a esta función |
| user-defined-string-literal | - |
la secuencia de caracteres
""
seguida, sin espacio, por la secuencia de caracteres que se convierte en el
sufijo-ud
|
ud-suffix
debe comenzar con el guion bajo
_
: los sufijos que no comienzan con el guion bajo están reservados para los operadores literales proporcionados por la biblioteca estándar. Tampoco puede contener dobles guiones bajos
__
: dichos sufijos también están reservados.
Si el operador literal es una plantilla, debe tener una lista de parámetros vacía y solo puede tener un parámetro de plantilla, que debe ser un paquete de parámetros de plantilla constante con tipo de elemento char (en cuyo caso se conoce como plantilla de operador literal numérico ):
template<char...> double operator ""_x();
|
o un parámetro de plantilla constante de tipo clase (en cuyo caso se conoce como plantilla de operador de literal de cadena ): struct A { constexpr A(const char*); }; template<A a> A operator ""_a(); |
(desde C++20) |
Solo se permiten las siguientes listas de parámetros en los operadores literales:
(
const
char
*
)
|
(1) | ||||||||
(
unsigned
long
long
int
)
|
(2) | ||||||||
(
long
double
)
|
(3) | ||||||||
(
char
)
|
(4) | ||||||||
(
wchar_t
)
|
(5) | ||||||||
(
char8_t
)
|
(6) | (desde 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) | (desde C++20) | |||||||
(
const
char16_t
*
,
std::size_t
)
|
(12) | ||||||||
(
const
char32_t
*
,
std::size_t
)
|
(13) | ||||||||
Argumentos predeterminados no están permitidos.
El language linkage de C no está permitido.
Aparte de las restricciones anteriores, los operadores literales y las plantillas de operadores literales son funciones normales (y plantillas de funciones), pueden declararse inline o constexpr, pueden tener vinculación interna o externa, pueden ser llamados explícitamente, se puede tomar sus direcciones, etc.
#include <string> void operator ""_km(long double); // OK, se llamará para 1.0_km void operator "" _km(long double); // igual al anterior, obsoleto std::string operator ""_i18n(const char*, std::size_t); // OK template<char...> double operator ""_pi(); // OK float operator ""_e(const char*); // OK // error: el sufijo debe comenzar con guion bajo float operator ""Z(const char*); // error: todos los nombres que comienzan con guion bajo seguido de letra mayúscula // están reservados (NOTA: un espacio entre "" y _). double operator"" _Z(long double); // OK. NOTA: no hay espacio entre "" y _. double operator""_Z(long double); // OK: los operadores literales pueden sobrecargarse double operator ""_Z(const char* args); int main() {}
Notas
Desde la introducción de literales definidos por el usuario, el código que utiliza constantes de macro de formato para tipos enteros de ancho fijo sin espacio después del literal de cadena precedente se volvió inválido: std:: printf ( "%" PRId64 " \n " , INT64_MIN ) ; debe ser reemplazado por std:: printf ( "%" PRId64 " \n " , INT64_MIN ) ; .
Debido al
maximal munch
, los literales enteros y de punto flotante definidos por el usuario que terminan en
p
,
P
,
(since C++17)
e
y
E
, cuando van seguidos de los operadores
+
o
-
, deben separarse del operador con espacios en blanco o paréntesis en el código fuente:
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
Lo mismo se aplica al operador punto que sigue a un literal definido por el usuario de tipo entero o de punto flotante:
#include <chrono> using namespace std::literals; auto a = 4s.count(); // Error auto b = 4s .count(); // OK auto c = (4s).count(); // OK
De lo contrario, se forma un único token de número de preprocesamiento inválido (por ejemplo, 1.0 _E + 2.0 o 4s. count ), lo que provoca que la compilación falle.
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_user_defined_literals
|
200809L
|
(C++11) | Literales definidos por el usuario |
Palabras clave
Ejemplos
#include <algorithm> #include <cstddef> #include <iostream> #include <numbers> #include <string> // utilizado como conversión de grados (parámetro de entrada) a radianes (salida devuelta) constexpr long double operator""_deg_to_rad(long double deg) { long double radians = deg * std::numbers::pi_v<long double> / 180; return radians; } // utilizado con tipo personalizado struct mytype { unsigned long long m; }; constexpr mytype operator""_mytype(unsigned long long n) { return mytype{n}; } // utilizado para efectos secundarios 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'; }
Salida:
1.570796 123 0x123ABC abcabc
Biblioteca estándar
Los siguientes operadores literales están definidos en la biblioteca estándar:
|
Definido en el espacio de nombres en línea
std::literals::complex_literals
|
|
|
un literal
std::complex
que representa un número puramente imaginario
(función) |
|
|
Definido en el espacio de nombres en línea
std::literals::chrono_literals
|
|
|
(C++14)
|
un literal
std::chrono::duration
que representa horas
(función) |
|
(C++14)
|
un literal
std::chrono::duration
que representa minutos
(función) |
|
(C++14)
|
un literal
std::chrono::duration
que representa segundos
(función) |
|
(C++14)
|
un literal
std::chrono::duration
que representa milisegundos
(función) |
|
(C++14)
|
un literal
std::chrono::duration
que representa microsegundos
(función) |
|
(C++14)
|
un literal
std::chrono::duration
que representa nanosegundos
(función) |
|
(C++20)
|
un literal
std::chrono::year
que representa un año particular
(función) |
|
(C++20)
|
un literal
std::chrono::day
que representa un día del mes
(función) |
|
Definido en el espacio de nombres en línea
std::literals::string_literals
|
|
|
(C++14)
|
convierte un literal de arreglo de caracteres a
basic_string
(función) |
|
Definido en el espacio de nombres en línea
std::literals::string_view_literals
|
|
|
(C++17)
|
crea una vista de cadena de un literal de arreglo de caracteres
(función) |
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Se aplica a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 1473 | C++11 |
se requería espacio en blanco entre
""
y
ud-suffix
en la
declaración de operadores literales |
se hizo opcional |
| CWG 1479 | C++11 | los operadores literales podían tener argumentos predeterminados | prohibido |
| CWG 2521 | C++11 |
operator
""
_Bq
estaba mal formado (no se requería
diagnóstico) porque usa el identificador reservado
_Bq
|
se deprecó la sintaxis del operador literal
con espacio en blanco entre "" y ud-suffix |