Espacios de nombres
Variantes
Acciones

Literales definidos por el usuario (desde C++11)

De cppreference.com
< cpp‎ | language
 
 
 
 


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)
1-4) Literales enteros definidos por el usuario, como 12_km.
5-6) Literales de punto flotante definidos por el usuario, como 0.5_Pa.
7) Literal de carácter definido por el usuario, como 'c'_X.
8) Literal de cadena definido por el usuario, como "abd"_L o u"xyz"_M.
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 ' opcionales entre dos dígitos cualquiera y se ignoran.

(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,

1) Para literales enteros definidos por el usuario:
a) si el conjunto de sobrecarga incluye un operador de literal con tipo de parámetro unsigned long long, la expresión literal definida por el usuario se trata como una llamada a función operator "" X(nULL), donde n es el literal sin sufijo-du;
b) de lo contrario, el conjunto de sobrecargas debe incluir uno, no ambos, entre un operador de literal sin formato o una plantilla de operador de literal numérico. Si el conjunto de sobrecargas incluye un operador de literal sin formato, la expresión literal definida por el usuario se trata como una llamada a la función operator "" X("n");
c) de lo contrario, si el conjunto de sobrecargas incluye una plantilla de operador de literal numérico, la expresión de literal definida por el usuario se trata como una llamada a la función operator "" X<'c1', 'c2', 'c3'..., 'ck'>(), donde c1..ck son los caracteres individuales de n y todos ellos son del juego de caracteres fuente básico (hasta C++23)juego de caracteres básico (hasta C++23).
2) Para literales de punto flotante definidos por el usuario:
a) si el conjunto de sobrecargas incluye un operador de literal con el parámetro tipo long double, la expresión literal definida por el usuario se trata como una llamada a la función operator "" X(fL), donde f es el literal sin sufijo-du;
b) de lo contrario, el conjunto de sobrecargas debe incluir uno, pero no ambos, de un operador de literal sin formato o plantilla de operador de literal numérico. Si el conjunto de sobrecargas incluye el operador de literal sin formato, la expresión literal definida por el usuario se trata como una llamada a la función operator "" X("f");
c) de lo contrario, si el conjunto de sobrecargas incluye una plantilla de literal numérico, la expresión de literal definida por el usuario se trata como una llamada a la función operator "" X<'c1', 'c2', 'c3'..., 'ck'>(), donde c1..ck son caracteres individuales de f y todos ellos son del juego de caracteres fuente básico (hasta C++23)juego de caracteres básico (hasta C++23).
3) Para los literales de cadena definidos por el usuario, siendo 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)
b) de lo contrario, la expresión de literal definida por el usuario se trata como una llamada a la función operator "" X (str, len), donde len es el tamaño del literal de cadena, excluyendo el carácter nulo de terminación.
4) Para los literales de carácter definidos por el usuario, la expresión literal defina por el usuario se trata como una llamada a la función operator "" X(ch), donde 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)
1) Los operadores de literal con esta lista de parámetros son los operadores de literal sin formato, usado como respaldo para literales definidos por el usuario enteros y de punto flotante (ver arriba).
2) Los operadores de literal con estas lista de parámetros son el operador de literal que se selecciona primero para los literales enteros definidos por el usuario.
3) Los operadores de literal estas listas de parámetros son el operador de literal que se selecciona como primera opción para los literales de punto flotante definidos por el usuario.
4-8) Operadores de literal con estas listas de parámetros se llaman para los literales de carácter definido por el usuario.
9-13) Operadores de literal con estas listas de parámetros se llaman para los literales de cadena definidos por el usuario.

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) [editar]
Definido en el espacio de nombres std::literals::chrono_literals
Un literal std::chrono::duration que representa horas.
(función) [editar]
Un literal std::chrono::duration que representa minutos.
(función) [editar]
Un literal std::chrono::duration que representa segundos.
(función) [editar]
Un literal std::chrono::duration que representa milisegundos.
(función) [editar]
Un literal std::chrono::duration que representa microsegundos.
(función) [editar]
Un literal std::chrono::duration que representa nanosegundos.
(función) [editar]
Un literal std::chrono::year que representa un año en particular.
(función) [editar]
Un literal std::chrono::day que representa un día de un mes.
(función) [editar]
Definido en el espacio de nombres std::literals::string_literals
Convierte un literal de array de caracteres a basic_string
(función) [editar]
Definido en el espacio de nombres std::literals::string_view_literals
Crea una vista sobre cadena a partir de un literal de array de caracteres.
(función) [editar]

[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.

[editar] Referencias

  1. Decimales: coma y punto son signos válidos