Literales definidos por el usuario (desde C++11)
Permite que los literales enteros, de punto[1] flotante, de carácter y de cadena produzcan objetos de tipo definido por el usuario mediante un sufijo definido por el usuario.
Contenido |
[editar] Sintaxis
Un literal definido por el usuario es una expresión de una de las siguientes formas:
literal-decimal sufijo-du | (1) | ||||||||
literal-octal sufijo-du | (2) | ||||||||
literal-hexadecimal sufijo-du | (3) | ||||||||
literal-binario sufijo-du | (4) | ||||||||
constante-fraccional exponentet(opcional) sufijo-du | (5) | ||||||||
secuencia-de-dígitos exponente sufijo-du | (6) | ||||||||
literal-de-carácter sufijo-du | (7) | ||||||||
literal-de-cadena sufijo-du | (8) | ||||||||
literal-decimal | - | Igual que en literal entero, un dígito decimal no cero seguido de cero o más dígitos decimales. |
literal-octal | - | Igual que en literal entero, un cero seguido de cero o más dígitos octales. |
literal-hexadecimal | - | Igual que en literal entero, 0x o 0X seguido de uno o más dígitos hexadecimales.
|
literal-binario | - | Igual que en literal entero, 0b o 0B seguido de uno o más dígitos binarios.
|
secuencia-de-dígitos | - | Igual que en literal de punto flotante, una secuencia de dígitos decimales.
. |
constante-fraccional | - | Igual que en literal de punto flotante, una secuencia-de-dígitos seguida de un punto (123.) o una secuencia-de-dígitos opcional seguida de un punto y otra secuencia-de-dígitos (1.0 o .12). |
exponente | - | Igual que en literal de punto flotante, la letra e o E seguido de un signo opcional, seguido de una secuencia-de-dígitos.
|
literal-de-carácter | - | Igual que en literal de carácter . |
literal-de-cadena | - | Igual que en literal de cadena, incluyendo literales de cadena sin formato |
sufijo-du | - | Un identificador, creado por una declaración de un operador de literal o una plantilla de operador de literal (véase a continuación). Todo sufijo-du insertado en un programa debe comenzar con el carácter de guion bajo _ . Los sufijo-du de la biblioteca estándar no comienzan con guiones bajos.
|
En las secuencias de dígitos enteros y de punto flotante se permiten separadores |
(desde C++14) |
Si un token coincide con una sintaxis de literal definida por el usuario y una sintaxis de literal regular, se entiende que es un literal regular (es decir, es imposible sobrecargar LL en 123LL).
Cuando el compilador encuentra un literal definido por el usuario con sufijo-du X
, realiza una búsqueda de nombres no calificados, buscando la función de nombre operator "" X
. Si la búsqueda no encuentra una declaración, el programa está mal formado. De lo contrario,
n
y todos ellos son del juego de caracteres fuente básico (hasta C++23)juego de caracteres básico (hasta C++23).f
es el literal sin sufijo-du;f
y todos ellos son del juego de caracteres fuente básico (hasta C++23)juego de caracteres básico (hasta C++23).str
el literal sin sufijo-du:
a) si el conjunto de sobrecargas incluye una plantilla de operador de literal de cadena con un parámetro de plantilla que no es un tipo para el que str es un argumento de plantilla bien formado, la expresión literal definida por el usuario se trata como una llamada a la función operator "" X<str>();
|
(desde C++20) |
len
es el tamaño del literal de cadena, excluyendo el carácter nulo de terminación.ch
es el literal sin sufijo-du: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 operador de literal que se pueda aplicar }
Cuando la concatenación de literales de cadena tiene lugar en la fase 6 de traducción, los literales de cadena definidos por el usuario también se concatenan, y sus sufijos-du se ignoran con el propósito de la concatenación, excepto que solo puede aparecer un sufijo en todos los literales concatenados:
int main() { L"A" "B" "C"_x; // correcto: igual a L"ABC"_x "P"_x "Q" "R"_y; // ERROR: dos sufijos-du diferentes (_x y _y) }
[editar] Operadores de literal
La función que se llama por un literal definido por el usuario se conoce como operador de literal (o, si es una plantilla, plantilla de operador de literal). Se declara como cualquier otra función o plantilla de función en un ámbito de espacio de nombres (también puede ser una función amiga, una instancia explícita o la especialización de una plantilla de función, o introducida por una declaración de uso), excepto las siguientes restricciones:
El nombre de esta función puede tener una de estas dos formas:
operator "" identificador
|
|||||||||
operator literal-de-cadena-definido-por-el-usuario
|
|||||||||
identificador | - | El identificador a utilizar como sufijo-du para los literales definidos por el usuario que llamarán a esta función. Debe comenzar con guion bajo _ : los sufijos que no comienzan con guion bajo están reservados para los operadores de literal proporcionados por la biblioteca estándar.
|
literal-de-cadena-definido-por-el-usuario | - | La secuencia de caracteres "" seguida, sin espacios, por la secuencia de caracteres que será el sufijo-du. Esta sintaxis especial permite utilizar palabras clave del lenguaje e identificadores reservados como sufijos-du, y se usa para la declaración de operator ""if en el encabezado <complex>. Tenga en cuenta que el uso de esta formato no cambia la regla de que los operadores de literal definidos por el usuario deben comenzar con un guion bajo: las declaraciones como operator ""if solo pueden aparecer como parte del encabezado de la biblioteca estándar. Sin embargo, permite el uso del guion bajo seguido de una letra mayúscula (que de lo contrario es un identificador reservado).
|
Si el operador de 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 de no tipo, con tipo de elemento char (en cuyo caso se conoce como 'plantilla de operador de literal numérico)
template <char...> double operator "" _x();
o un parámetro de plantilla sin tipo 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) |
Solamente se permiten las siguientes listas de parámetros en operadores de literal :
( 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) | ||||||||
No se permiten argumentos por defecto.
No se permite enlazado de lenguaje de C.
Aparte de las restricciones anteriores, los operadores de literal y las plantillas de operadores de literal son funciones normales (y plantillas de función), se pueden declarar inline o constexpr, pueden tener enlaces internos o externos, se pueden llamar explícitamente, se puede obtener sus direcciones, etc.
#include <string> void operator "" _km(long double); // correcto, se llama para 1.0_km std::string operator "" _i18n(const char*, std::size_t); // correcto template <char...> double operator "" _π(); // correcto float operator ""_e(const char*); // correcto // ERROR: el sufijo debe empezar 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); // correcto. NOTA: sin espacio entre "" y _. double operator""_Z(long double); // correcto: se pueden sobrecargar los operadores de literal double operator ""_Z(const char* args); int main() {}
[editar] Notas
Desde la introducción de los literales definidos por el usuario, el código que usa 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); se tiene cambiar por std::printf("%" PRId64"\n",INT64_MIN);
Debido al maximal munch, los literales enteros y de punto flotante definidos por el usuario terminados en p
, P
, (desde C++17) e
y E
, cunado van seguidos de los operadores +
o -
, se deben separar el 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; // correcto auto z = 1.0_E +2.0; // correcto auto q = (1.0_E)+2.0; // correcto auto w = 1_p+2; // error auto u = 1_p +2; // correcto
Lo mismo se aplica al operador punto que sigue a un literal entero o de punto flotante definido por el usuario:
#include <chrono> using namespace std::literals; auto a = 4s.count(); // ERROR auto b = 4s .count(); // correcto auto c = (4s).count(); // correcto
De lo contrario, se forma un único token numérico de preprocesamiento no válido (por ejemplo, 1.0_E+2.0 o 4s.count), que causa un fallo de la compilación.
[editar] Ejemplos
#include <cstddef> #include <algorithm> #include <iostream> #include <numbers> #include <string> // se usa para conversión de grados (parámetro de entrada) a radianes (salida) constexpr long double operator"" _gra_a_rad ( long double grad ) { long double radianes = grad * std::numbers::pi_v<long double> / 180; return radianes; } // usado con tipo personalizado struct mitipo { unsigned long long m; }; constexpr mitipo operator"" _mitipo ( unsigned long long n ) { return mitipo{n}; } // utilizado para efectos secundarios void operator"" _escribir ( 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 plantilla de operador de literal de cadena template<std::size_t N> struct DoubleString { char p[N*2-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_gra_a_rad; std::cout << std::fixed << x_rad << '\n'; mitipo y = 123_mitipo; std::cout << y.m << '\n'; 0x123ABC_escribir; std::cout << "abc"_x2 << '\n'; }
Salida:
1.570796 123 0x123ABC abcabc
[editar] Biblioteca estándar
En la biblioteca estándar se definen los siguiente operadores de literal
Definido en el espacio de nombres
std::literals::complex_literals | |
Un literal std::complex que representa un número imaginario puro. (función) | |
Definido en el espacio de nombres
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 en particular. (función) |
(C++20) |
Un literal std::chrono::day que representa un día de un mes. (función) |
Definido en el espacio de nombres
std::literals::string_literals | |
(C++14) |
Convierte un literal de array de caracteres a basic_string (función) |
Definido en el espacio de nombres
std::literals::string_view_literals | |
(C++17) |
Crea una vista sobre cadena a partir de un literal de array de caracteres. (función) |
[editar] Informes de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
CWG 1473 | C++11 | En la declaración de los operadores de literal se requiere un espacio en blanco entre "" y sufijo-du. | Se hace opcional. |