sirve que programa para lenguaje funciones ejemplos comandos caracteristicas c++ c++11 user-defined-literals

programa - para que sirve c++



¿Qué nuevas capacidades agregan los literales definidos por el usuario a C++? (12)

A primera vista, parece ser un simple azúcar sintáctico.

Pero cuando miramos más profundo, vemos que es más que azúcar sintáctica, ya que amplía las opciones del usuario de C ++ para crear tipos definidos por el usuario que se comportan exactamente como distintos tipos incorporados. En esto, esta pequeña "bonificación" es una adición de C ++ 11 muy interesante a C ++.

¿Realmente lo necesitamos en C ++?

Veo algunos usos en el código que escribí en los últimos años, pero el hecho de que no lo haya usado en C ++ no significa que no sea interesante para otro desarrollador de C ++ .

Habíamos usado en C ++ (y en C, supongo) literales definidos por el compilador, para escribir números enteros como enteros cortos o largos, números reales como flotante o doble (o incluso doble largo) y cadenas de caracteres como caracteres normales o anchos .

En C ++, tuvimos la posibilidad de crear nuestros propios tipos (es decir, clases), con posibles gastos indirectos (en línea, etc.). Tuvimos la posibilidad de agregar operadores a sus tipos, para que se comportaran como tipos incorporados similares, lo que permite a los desarrolladores de C ++ usar matrices y números complejos con tanta naturalidad como lo hubieran hecho si se hubieran agregado al lenguaje en sí. Incluso podemos agregar operadores de transmisión (lo que generalmente es una mala idea, pero a veces, es la solución correcta).

Todavía nos falta algo para que los tipos de usuario se comporten como tipos incorporados: literales definidos por el usuario.

Por lo tanto, supongo que es una evolución natural para el lenguaje, pero para ser lo más completa posible: " Si desea crear un tipo, y desea que se comporte tanto como sea posible con los tipos integrados, estas son las herramientas. .. "

Supongo que es muy similar a la decisión de .NET de convertir cada primitiva en una estructura, incluidos booleanos, enteros, etc., y todas las estructuras derivan de Object. Esta decisión por sí sola pone a .NET mucho más allá del alcance de Java cuando se trabaja con elementos primitivos, sin importar cuántos hacks de box / unboxing Java agregará a su especificación.

¿Realmente lo necesitas en C ++?

Esta pregunta es para que USTED responda. No es Bjarne Stroustrup. No Herb Sutter. No es lo que sea miembro del comité estándar de C ++. Esta es la razón por la que tiene la opción en C ++ , y no restringirán una notación útil a los tipos incorporados solo.

Si lo necesita, entonces es una adición bienvenida. Si no lo haces, bueno ... No lo uses. No te costará nada.

Bienvenido a C ++, el idioma donde las características son opcionales.

¿¿¿Hinchado??? ¡Muéstrame tus complejos!

Hay una diferencia entre hinchado y complejo (juego de palabras).

Como lo muestra Niels en ¿Qué nuevas capacidades agregan los literales definidos por el usuario a C ++? , poder escribir un número complejo es una de las dos características agregadas "recientemente" a C y C ++:

// C89: MyComplex z1 = { 1, 2 } ; // C99: You''ll note I is a macro, which can lead // to very interesting situations... double complex z1 = 1 + 2*I; // C++: std::complex<double> z1(1, 2) ; // C++11: You''ll note that "i" won''t ever bother // you elsewhere std::complex<double> z1 = 1 + 2_i ;

Ahora, tanto el tipo "doble complejo" C99 como el tipo "std :: complex" C ++ pueden multiplicarse, agregarse, restarse, etc., utilizando la sobrecarga del operador.

Pero en C99, simplemente agregaron otro tipo como un tipo incorporado y soporte integrado para sobrecarga del operador. Y agregaron otra característica literal incorporada.

En C ++, simplemente usaron las características existentes del lenguaje, vieron que la característica literal era una evolución natural del lenguaje, y así se agregó.

En C, si necesita la misma mejora de notación para otro tipo, no tiene suerte hasta su cabildeo para agregar sus funciones de onda cuántica (o puntos 3D, o cualquier tipo básico que esté utilizando en su campo de trabajo) al El estándar C como tipo incorporado tiene éxito.

En C ++ 11, puede hacerlo usted mismo:

Point p = 25_x + 13_y + 3_z ; // 3D point

¿Está hinchado? No , la necesidad está ahí, como lo muestra cómo los complejos C y C ++ necesitan una forma de representar sus valores complejos literales.

¿Está mal diseñado? No , está diseñado como cualquier otra característica de C ++, con extensibilidad en mente.

¿Es solo para fines de notación? No , ya que incluso puede agregar seguridad de tipo a su código.

Por ejemplo, imaginemos un código orientado a CSS:

css::Font::Size p0 = 12_pt ; // Ok css::Font::Size p1 = 50_percent ; // Ok css::Font::Size p2 = 15_px ; // Ok css::Font::Size p3 = 10_em ; // Ok css::Font::Size p4 = 15 ; // ERROR : Won''t compile !

Entonces es muy fácil imponer un tipado fuerte a la asignación de valores.

¿Es peligroso?

Buena pregunta. ¿Pueden estas funciones ser espacios de nombres? Si es así, entonces Jackpot!

De todos modos, como todo, puedes matarte si una herramienta se usa de forma incorrecta . C es poderoso, y puedes dispararle a la cabeza si malgastas la pistola C. C ++ tiene la pistola C, pero también el bisturí, la Taser y cualquier otra herramienta que encuentres en el juego de herramientas. Puedes usar mal el bisturí y desangrarte hasta la muerte. O puedes construir un código muy elegante y robusto.

Entonces, como cada función de C ++, ¿realmente la necesitas? Es la pregunta que debe responder antes de usarla en C ++. Si no lo hace, no le costará nada. Pero si realmente lo necesitas, al menos, el idioma no te defraudará.

El ejemplo de la fecha?

Tu error, me parece, es que estás mezclando operadores:

1974/01/06AD ^ ^ ^

Esto no se puede evitar, porque / siendo un operador, el compilador debe interpretarlo. Y, AFAIK, es algo bueno.

Para encontrar una solución para su problema, escribiría el literal de alguna otra manera. Por ejemplo:

"1974-01-06"_AD ; // ISO-like notation "06/01/1974"_AD ; // french-date-like notation "jan 06 1974"_AD ; // US-date-like notation 19740106_AD ; // integer-date-like notation

Personalmente, elegiría el número entero y las fechas ISO, pero depende de SUS necesidades. Que es el punto de dejar que el usuario defina sus propios nombres literales.

C++11 introduce literales definidos por el usuario que permitirán la introducción de una nueva sintaxis literal basada en literales existentes ( int , hex , string , float ) para que cualquier tipo pueda tener una presentación literal.

Ejemplos:

// imaginary numbers std::complex<long double> operator "" _i(long double d) // cooked form { return std::complex<long double>(0, d); } auto val = 3.14_i; // val = complex<long double>(0, 3.14) // binary values int operator "" _B(const char*); // raw form int answer = 101010_B; // answer = 42 // std::string std::string operator "" _s(const char* str, size_t /*length*/) { return std::string(str); } auto hi = "hello"_s + " world"; // + works, "hello"_s is a string not a pointer // units assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

A primera vista, esto se ve muy bien, pero me pregunto qué tan aplicable es realmente, cuando traté de pensar en tener los sufijos _AD y _BC crear fechas, encontré que es problemático debido al orden del operador. 1974/01/06_AD primero evaluaría 1974/01 (como plain int s) y solo después 06_AD (por no mencionar que agosto y septiembre deben escribirse sin el 0 por razones ocultas). Esto puede 1974-1/6_AD teniendo la sintaxis 1974-1/6_AD para que la orden de evaluación del operador funcione, pero es torpe.

Entonces, ¿a qué se reduce mi pregunta? ¿Sientes que esta característica se justificará a sí misma? ¿Qué otros literales le gustaría definir que harán que su código C ++ sea más legible?

Sintaxis actualizada para adaptarse al borrador final en junio de 2011


Aquí hay un caso donde hay una ventaja de usar literales definidos por el usuario en lugar de una llamada de constructor:

#include <bitset> #include <iostream> template<char... Bits> struct checkbits { static const bool valid = false; }; template<char High, char... Bits> struct checkbits<High, Bits...> { static const bool valid = (High == ''0'' || High == ''1'') && checkbits<Bits...>::valid; }; template<char High> struct checkbits<High> { static const bool valid = (High == ''0'' || High == ''1''); }; template<char... Bits> inline constexpr std::bitset<sizeof...(Bits)> operator"" _bits() noexcept { static_assert(checkbits<Bits...>::valid, "invalid digit in binary string"); return std::bitset<sizeof...(Bits)>((char []){Bits..., ''/0''}); } int main() { auto bits = 0101010101010101010101010101010101010101010101010101010101010101_bits; std::cout << bits << std::endl; std::cout << "size = " << bits.size() << std::endl; std::cout << "count = " << bits.count() << std::endl; std::cout << "value = " << bits.to_ullong() << std::endl; // This triggers the static_assert at compile time. auto badbits = 2101010101010101010101010101010101010101010101010101010101010101_bits; // This throws at run time. std::bitset<64> badbits2("2101010101010101010101010101010101010101010101010101010101010101_bits"); }

La ventaja es que una excepción en tiempo de ejecución se convierte en un error en tiempo de compilación. No se pudo agregar la afirmación estática al conjunto de bits que toma una cadena (al menos no sin argumentos de plantilla de cadena).


Bjarne Stroustrup habla de UDL en esta charla de C ++ 11 , en la primera sección sobre interfaces de tipo rico, alrededor de 20 minutos.

Su argumento básico para UDLs toma la forma de un silogismo:

  1. Los tipos "triviales", es decir, los tipos primitivos integrados, solo pueden detectar errores de tipo trivial. Las interfaces con tipos más ricos permiten que el sistema de tipos atrape más tipos de errores.

  2. Los tipos de errores de tipo que puede capturar el código de gran riqueza tienen un impacto en el código real. (Da el ejemplo de Mars Climate Orbiter, que fracasó infamemente debido a un error de dimensiones en una constante importante).

  3. En código real, las unidades rara vez se usan. La gente no los usa, porque incurrir en el tiempo de ejecución o la sobrecarga de memoria para crear tipos ricos es demasiado costoso, y el uso del código preexistente de la unidad de plantillas C ++ es tan notoriamente feo que nadie lo usa. (Empíricamente, nadie lo usa, a pesar de que las bibliotecas han existido por una década).

  4. Por lo tanto, para que los ingenieros pudieran usar unidades en código real, necesitábamos un dispositivo que (1) no incurriera en sobrecarga de tiempo de ejecución y (2) es notablemente aceptable.


C ++ suele ser muy estricto con respecto a la sintaxis utilizada; salvo el preprocesador, no hay mucho que pueda usar para definir una sintaxis / gramática personalizada. Por ejemplo, podemos sobrecargar operatos existentes, pero no podemos definir nuevos - IMO está muy en sintonía con el espíritu de C ++.

No me molestan algunas formas de código fuente más personalizado, pero el punto elegido me parece muy aislado, lo que me confunde más.

Incluso el uso previsto puede hacer que sea mucho más difícil leer el código fuente: una sola letra puede tener efectos secundarios de amplio alcance que de ninguna manera pueden identificarse en el contexto. Con simetría a u, l y f, la mayoría de los desarrolladores elegirán letras individuales.

Esto también puede convertir el alcance en un problema, el uso de letras sueltas en el espacio de nombres global probablemente se considere una mala práctica, y las herramientas que se supone que mezclan bibliotecas más fácilmente (espacios de nombres e identificadores descriptivos) probablemente anularán su propósito.

Veo algo de mérito en combinación con "auto", también en combinación con una unidad de biblioteca como unidades impulsoras , pero no lo suficiente como para merecer esta adición.

Sin embargo, me pregunto qué ingeniosas ideas se nos ocurren.


El ruido de línea en esa cosa es enorme. También es horrible de leer.

Avísame, ¿razonaron esa nueva sintaxis adicional con algún tipo de ejemplos? Por ejemplo, ¿tienen algunos programas que ya usan C ++ 0x?

Para mí, esta parte:

auto val = 3.14_i

No justifica esta parte:

std::complex<double> operator ""_i(long double d) // cooked form { return std::complex(0, d); }

Ni siquiera si usas la i-sintaxis en otras 1000 líneas también. Si escribe, probablemente también escriba 10000 líneas de algo más a lo largo de eso. Especialmente cuando todavía probablemente escribirás principalmente en todas partes esto:

std::complex<double> val = 3.14i

''auto'' -keyword puede estar justificado, solo quizás. Pero tomemos solo C ++, porque es mejor que C ++ 0x en este aspecto.

std::complex<double> val = std::complex(0, 3.14);

Es como ... así de simple. Incluso pensé que todos los paréntesis estándar y puntiagudos son simplemente cojos si los usas en todas partes. No empiezo a adivinar qué sintaxis hay en C ++ 0x para convertir std :: complex en complejo.

complex = std::complex<double>;

Quizás sea algo sencillo, pero no creo que sea tan simple en C ++ 0x.

typedef std::complex<double> complex; complex val = std::complex(0, 3.14);

¿Quizás? > :)

De todos modos, el punto es: escribir 3.14i en lugar de std :: complex (0, 3.14); no le ahorra mucho tiempo en general, excepto en algunos casos súper especiales.


Es muy bueno para el código matemático. De mi mente puedo ver el uso de los siguientes operadores:

grados para grados. Eso hace que escribir en ángulos absolutos sea mucho más intuitivo.

double operator ""_deg(long double d) { // returns radians return d*M_PI/180; }

También se puede usar para varias representaciones de puntos fijos (que todavía están en uso en el campo de DSP y gráficos).

int operator ""_fix(long double d) { // returns d as a 1.15.16 fixed point number return (int)(d*65536.0f); }

Estos parecen buenos ejemplos de cómo usarlo. Ayudan a hacer que las constantes en el código sean más legibles. Es otra herramienta para hacer que el código sea ilegible también, pero ya tenemos tantas herramientas de abuso que uno más no duele demasiado.


Hmm ... No he pensado en esta característica todavía. Tu muestra estuvo bien pensada y es ciertamente interesante. C ++ es muy poderoso como lo es ahora, pero desafortunadamente la sintaxis utilizada en las piezas de código que lee es a veces demasiado compleja. La legibilidad es, sino todo, al menos mucho. Y tal característica estaría orientada para una mayor legibilidad. Si tomo tu último ejemplo

assert(1_kg == 2.2_lb); // give or take 0.00462262 pounds

... Me pregunto cómo expresarías eso hoy. Tendría una clase KG y una LB y compararía objetos implícitos:

assert(KG(1.0f) == LB(2.2f));

Y eso también lo haría. Con los tipos que tienen nombres o tipos más largos que no tienen esperanzas de tener un constructor tan agradable para escribir un adaptador, podría ser una buena adición para la creación e inicialización de objetos implícitos sobre la marcha. Por otro lado, también puede crear e inicializar objetos utilizando métodos.

Pero estoy de acuerdo con Nils en matemáticas. Las funciones de trigonometría C y C ++, por ejemplo, requieren entrada en radianes. Creo que en grados, por lo que una conversión implícita muy corta como Nils publicado es muy agradable.

Sin embargo, en última instancia, será azúcar sintáctico, pero tendrá un ligero efecto sobre la legibilidad. Y probablemente sea más fácil escribir algunas expresiones también (sin (180.0deg) es más fácil de escribir que sin (deg (180.0)). Y habrá personas que abusen del concepto. Pero entonces, las personas que abusan del lenguaje deberían usar lenguas muy restrictivas en lugar de algo tan expresivo como C ++.

Ah, mi publicación básicamente no dice nada: va a estar bien, el impacto no será demasiado grande. No nos preocupemos :-)


La única justificación requerida es admitir la verificación de dimensiones en tiempo de compilación.

auto force = 2_N; auto dx = 2_m; auto energy = force * dx; assert(energy == 4_J);

Véase, por ejemplo, PhysUnits-CT-Cpp11 , una pequeña biblioteca de solo cabecera C ++ 11, C ++ 14 para el análisis dimensional en tiempo de compilación y la manipulación y conversión unidad / cantidad. Más simple que Boost.Units , admite literales de símbolos de unidad como m, g, s, prefijos métricos como m, k, M, solo depende de la biblioteca C ++ estándar, solo SI, potencias integrales de dimensiones.


Los UDL tienen un espacio de nombres (y se pueden importar mediante el uso de declaraciones / directivas, pero no se puede explícitamente 3.14std::i un literal como 3.14std::i ), lo que significa que (afortunadamente) no habrá una tonelada de conflictos.

El hecho de que puedan ser modelados (y consolidados) significa que puedes hacer algunas cosas bastante poderosas con los UDL. Los autores de Bigint estarán muy contentos, ya que finalmente pueden tener constantes arbitrariamente grandes, calculadas en tiempo de compilación (a través de constexpr o plantillas).

Estoy triste de que no veamos un par de literales útiles en el estándar (como se ve), como s para std::string y i para la unidad imaginaria.

La cantidad de tiempo de codificación que los UDL ahorrarán en realidad no es tan alto, pero la legibilidad aumentará enormemente y cada vez se pueden pasar más cálculos al tiempo de compilación para una ejecución más rápida.


Nunca he necesitado o deseado esta característica (pero este podría ser el efecto Blub ). Mi reacción instintiva es que es poco convincente, y es probable que atraiga a las mismas personas que piensan que es genial sobrecargar al operador + por cualquier operación que pueda interpretarse de forma remota como una adición.


Permítanme agregar un poco de contexto. Para nuestro trabajo, los literales definidos por el usuario son muy necesarios. Trabajamos en MDE (Ingeniería Dirigida por Modelos). Queremos definir modelos y metamodelos en C ++. De hecho, implementamos un mapeo de Ecore a C ++ ( EMF4CPP ).

El problema surge cuando se pueden definir elementos del modelo como clases en C ++. Estamos adoptando el enfoque de transformar el metamodelo (Ecore) en plantillas con argumentos. Los argumentos de la plantilla son las características estructurales de los tipos y las clases. Por ejemplo, una clase con dos atributos int sería algo así como:

typedef ::ecore::Class< Attribute<int>, Attribute<int> > MyClass;

Sin embargo, resulta que cada elemento en un modelo o metamodelo, por lo general tiene un nombre. Nos gustaría escribir:

typedef ::ecore::Class< "MyClass", Attribute< "x", int>, Attribute<"y", int> > MyClass;

PERO, C ++, ni C ++ 0x no permiten esto, ya que las cadenas están prohibidas como argumentos para las plantillas. Puedes escribir el nombre char por char, pero esto es un desastre. Con los literales adecuados definidos por el usuario, podríamos escribir algo similar. Digamos que usamos "_n" para identificar los nombres de los elementos del modelo (no utilizo la sintaxis exacta, solo para hacer una idea):

typedef ::ecore::Class< MyClass_n, Attribute< x_n, int>, Attribute<y_n, int> > MyClass;

Finalmente, tener esas definiciones como plantillas nos ayuda mucho a diseñar algoritmos para atravesar los elementos del modelo, transformaciones del modelo, etc. que son realmente eficientes, porque el compilador determina la información del tipo, la identificación, las transformaciones, etc. en el momento de la compilación.


Usé literales de usuario para cadenas binarias como esta:

"asd/0/0/0/1"_b

usando el constructor std::string(str, n) para que /0 no corte la cadena a la mitad. (El proyecto hace mucho trabajo con varios formatos de archivo).

Esto fue útil también cuando abandoné std::string a favor de un wrapper para std::vector .