C ++ 11 Presenta literales definidos por el usuario que permitirán la introducción de nueva sintaxis literal basado en literales existentes ( int
, hex
, string
, float
) de modo que cualquier tipo serán capaces de 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, descubrí que es problemático debido al orden del operador. 1974/01/06_AD
primero evaluaría 1974/01
(como simple int
) y solo más tarde el 06_AD
(sin mencionar que agosto y septiembre tienen que escribirse sin el 0
por razones octales). Esto se puede solucionar haciendo que la sintaxis sea 1974-1/6_AD
para que el orden de evaluación del operador funcione pero sea torpe.
Entonces, mi pregunta se reduce a esto, ¿crees que esta característica se justificará? ¿Qué otros literales le gustaría definir que harán que su código C ++ sea más legible?
Sintaxis actualizada para ajustarse al borrador final en junio de 2011
fuente
string operator "" _s(const char*s);"
no se puede usar para analizar"hello"_s"
. Este es un literal de cadena y buscará el operador con unsize_t
parámetro adicional . Estoy en lo cierto?uint16_t
cuyo comportamiento depende de la implementación, con tipos similaresuwrap16
yunum16
cuyo comportamiento sería independiente de la implementación, de modo que dadasuwrap16 w=1; unum16 n=1;
las expresionesw-2
yn-2
produciría(uwrap16)65535
y(int)-1
, respectivamente [uint16_t
produciría el primer resultado en sistemas dondeint
es de 16 bits, y el segundo en sistemas dondeint
es más grande]. El mayor problema que vi fue el manejo de literales numéricos.sizeof
tipos de enteros devueltos dependen de la implementación, pero la situación aún podría mejorarse mucho más de lo que es. ¿Qué pensarías de ese concepto?Respuestas:
Aquí hay un caso en el que hay una ventaja de usar literales definidos por el usuario en lugar de una llamada de constructor:
La ventaja es que una excepción en tiempo de ejecución se convierte en un error en tiempo de compilación. No podría agregar la aserción estática al controlador de conjunto de bits que toma una cadena (al menos no sin argumentos de plantilla de cadena).
fuente
A primera vista, parece ser un azúcar sintáctico simple.
Pero cuando miramos más profundamente, 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 los distintos tipos integrados. En esto, este pequeño "bonus" es una adición muy interesante de C ++ 11 a C ++.
¿Realmente lo necesitamos en C ++?
Veo pocos usos en el código que escribí en los últimos años, pero solo porque no lo use 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 ++, teníamos la posibilidad de crear nuestros propios tipos (es decir, clases), potencialmente sin sobrecarga (en línea, etc.). Tuvimos la posibilidad de agregar operadores a sus tipos, para que se comporten como tipos incorporados similares, lo que permite a los desarrolladores de C ++ usar matrices y números complejos tan naturalmente como lo harían si se hubieran agregado al lenguaje en sí. Incluso podemos agregar operadores de conversión (que generalmente es una mala idea, pero a veces, es la solución correcta).
Todavía nos faltó una cosa 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 completo posible: " Si desea crear un tipo, y desea que se comporte lo más posible que los tipos incorporados, aquí están las herramientas. .. "
Supongo que es muy similar a la decisión de .NET de hacer que cada primitiva sea una estructura, incluidos booleanos, enteros, etc., y que todas las estructuras se deriven de Object. Esta decisión por sí sola pone a .NET mucho más allá del alcance de Java cuando se trabaja con primitivas, sin importar la cantidad de hacks de boxeo / unboxing que Java agregará a su especificación.
¿Realmente lo necesitas en C ++?
Esta pregunta es para USTED contestar. No Bjarne Stroustrup. No Herb Sutter. No es el miembro del comité estándar de C ++. Es por eso que tiene la opción en C ++ , y no restringirán una notación útil solo a los tipos integrados.
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 lenguaje 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 ++:
Ahora, tanto el tipo "doble complejo" C99 como el tipo "std :: complex" de C ++ pueden multiplicarse, sumarse, restarse, etc., utilizando la sobrecarga del operador.
Pero en C99, simplemente agregaron otro tipo como un tipo incorporado y soporte de sobrecarga del operador incorporado. Y agregaron otra característica literal incorporada.
En C ++, solo usaron las características existentes del lenguaje, vieron que la característica literal era una evolución natural del lenguaje y, por lo tanto, la agregaron.
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é usando en su campo de trabajo) al El estándar C como tipo incorporado tiene éxito.
En C ++ 11, puedes hacerlo tú mismo:
¿Está hinchado? No , la necesidad está ahí, como se muestra por 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 ++, teniendo en cuenta la extensibilidad.
¿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:
Entonces es muy fácil imponer un tipeo fuerte a la asignación de valores.
¿Es peligroso?
Buena pregunta. ¿Pueden estas funciones tener espacios de nombres? Si es así, entonces Jackpot!
De todos modos, como todo, puedes matarte si una herramienta se usa incorrectamente . C es poderoso, y puedes dispararte si usas mal la pistola C. C ++ tiene la pistola C, pero también el bisturí, la pistola y cualquier otra herramienta que encuentres en el kit de herramientas. Puedes usar mal el bisturí y desangrarte hasta morir. O puede crear un código muy elegante y robusto.
Entonces, como todas las funciones de C ++, ¿realmente lo necesita? Es la pregunta que debe responder antes de usarlo en C ++. Si no lo hace, no le costará nada. Pero si realmente lo necesita, al menos, el idioma no lo defraudará.
El ejemplo de la fecha?
Me parece que su error es que está mezclando operadores:
Esto no se puede evitar, porque / siendo un operador, el compilador debe interpretarlo. Y, AFAIK, es algo bueno.
Para encontrar una solución a su problema, escribiría el literal de alguna otra manera. Por ejemplo:
Personalmente, elegiría el entero y las fechas ISO, pero depende de SUS necesidades. Cuál es el objetivo de dejar que el usuario defina sus propios nombres literales.
fuente
you can write 1+2i, but you still can't write a+bi, so there's absolutely no point
incluso ignorar sua+bi
ejemplo es ridículo, el hecho de que lo perciba como "baja frecuencia" no significa que todos lo hagan. . . Mirando el panorama general, el punto es asegurarse de que los objetos definidos por el usuario puedan ser considerados como ciudadanos de primera clase del idioma, al igual que los tipos incorporados. Entonces, si puedes escribir1.5f
y1000UL
, ¿por qué no podrías escribir25i
o incluso100101b
? Al contrario de C y Java, los tipos de usuario no deben considerarse ciudadanos de segunda clase del lenguaje en C ++.Most of data still comes from IO
Hay muchos valores codificados en el código. Mire todos los booleanos, todos los enteros, todos los dobles que vienen en el código, porque es más conveniente escribir enx = 2 * y ;
lugar dex = Two * y
dondeTwo
es una constante fuertemente tipada . Los literales definidos por el usuario nos permiten ponerle un tipo y escribir:x = 2_speed * y ;
y hacer que el compilador verifique que el cálculo tenga sentido. . . Todo se trata de escribir fuerte. . . Quizás no lo uses. Pero seguro que lo haré, tan pronto como pueda usar un compilador habilitado para C ++ 11 en el trabajo.Es muy bueno para el código matemático. Fuera de mi mente puedo ver el uso de los siguientes operadores:
grados por grados. Eso hace que escribir ángulos absolutos sea mucho más intuitivo.
También se puede usar para varias representaciones de punto fijo (que todavía están en uso en el campo de DSP y gráficos).
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 también sea ilegible, pero ya tenemos tantas herramientas abusadas que una más no hace mucho daño.
fuente
Los UDL tienen espacios de nombres (y pueden importarse mediante declaraciones / directivas, pero no puede explícitamente el espacio de nombres un literal como
3.14std::i
), lo que significa que (con suerte) no habrá un montón de enfrentamientos.El hecho de que en realidad puedan ser moldeados (y constexpr'd) significa que puedes hacer algunas cosas bastante poderosas con 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 porque no veremos un par de literales útiles en el estándar (por lo que parece), como
s
porstd::string
yi
para la unidad imaginaria.La cantidad de tiempo de codificación que los UDL guardarán en realidad no es tan alta, pero la legibilidad aumentará enormemente y cada vez más cálculos se pueden cambiar al tiempo de compilación para una ejecución más rápida.
fuente
Déjame agregar un poco de contexto. Para nuestro trabajo, los literales definidos por el usuario son muy necesarios. Trabajamos en MDE (Model-Driven Engineering). Queremos definir modelos y metamodelos en C ++. De hecho, implementamos una asignación 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 clases. Por ejemplo, una clase con dos atributos int sería algo como:
Sin embargo, resulta que cada elemento en un modelo o metamodelo, generalmente tiene un nombre. Nos gustaría escribir:
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 ciertamente un desastre. Con literales definidos por el usuario adecuados, podríamos escribir algo similar. Digamos que usamos "_n" para identificar nombres de elementos de modelo (no uso la sintaxis exacta, solo para hacer una idea):
Finalmente, tener esas definiciones como plantillas nos ayuda mucho a diseñar algoritmos para atravesar los elementos del modelo, las transformaciones del modelo, etc. que son realmente eficientes, porque el compilador determina la información de tipo, identificación, transformaciones, etc. en tiempo de compilación.
fuente
by the compiler at compile time
parte ... :-)Bjarne Stroustrup habla sobre los UDL en esta charla de C ++ 11 , en la primera sección sobre interfaces ricas en tipos, alrededor de 20 minutos.
Su argumento básico para UDL toma la forma de un silogismo:
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 detecte más tipos de errores.
Los tipos de errores de tipo que puede captar un código escrito de forma rica pueden tener un impacto en el código real. (Da el ejemplo del Mars Climate Orbiter, que falló infamemente debido a un error de dimensiones en una constante importante).
En código real, las unidades rara vez se usan. La gente no los usa, porque incurrir en tiempo de ejecución o sobrecarga de memoria para crear tipos ricos es demasiado costoso, y usar un código de unidad con plantilla C ++ preexistente es tan feo que nadie lo usa. (Empíricamente, nadie lo usa, a pesar de que las bibliotecas han existido durante una década).
Por lo tanto, para que los ingenieros usen unidades en código real, necesitábamos un dispositivo que (1) no incurre en gastos generales de tiempo de ejecución y (2) es notoriamente aceptable.
fuente
La verificación de dimensiones en tiempo de compilación es la única justificación requerida.
Véase, por ejemplo, PhysUnits-CT-Cpp11 , una pequeña biblioteca de solo encabezado C ++ 11, C ++ 14 para el análisis dimensional en tiempo de compilación y la manipulación y conversión de unidades / cantidades. 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 estándar de C ++, solo SI, potencias integrales de dimensiones.
fuente
Hmm ... todavía no he pensado en esta característica. Su muestra fue bien pensada y es ciertamente interesante. C ++ es muy poderoso como lo es ahora, pero desafortunadamente la sintaxis utilizada en los fragmentos de código que lee es a veces demasiado compleja. La legibilidad es, si no toda, al menos mucho. Y tal característica estaría orientada a una mayor legibilidad. Si tomo tu último ejemplo
... Me pregunto cómo expresarías eso hoy. Tendría una clase KG y una clase LB y compararía los objetos implícitos:
Y eso también funcionaría. Con tipos que tienen nombres más largos o tipos que no tiene esperanzas de tener un buen constructor 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. Sin embargo, pienso en grados, por lo que una conversión implícita muy corta como la publicada por Nils es muy buena.
Sin embargo, en última instancia, será azúcar sintáctico, pero tendrá un ligero efecto sobre la legibilidad. Y probablemente también será más fácil escribir algunas expresiones (sin (180.0deg) es más fácil de escribir que sin (deg (180.0)). Y luego habrá personas que abusen del concepto. Pero entonces, las personas que abusan del lenguaje deberían usar lenguajes muy restrictivos en lugar de algo tan expresivo como C ++.
Ah, mi publicación dice básicamente nada, excepto: todo va a estar bien, el impacto no será demasiado grande. No nos preocupemos. :-)
fuente
Nunca he necesitado o deseado esta función (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 a operator + para cualquier operación que pueda interpretarse remotamente como una adición.
fuente
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 los operadores existentes, pero no podemos definir los nuevos. En mi opinión, esto está muy en sintonía con el espíritu de C ++.
No me importan 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 gran alcance que de ninguna manera se pueden identificar desde el contexto. Con simetría para u, l y f, la mayoría de los desarrolladores elegirán letras simples.
Esto también puede convertir el alcance en un problema, el uso de letras individuales en el espacio de nombres global probablemente se considerará una mala práctica, y las herramientas que supuestamente 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 biblioteca de unidades como unidades de impulso , pero no lo suficiente como para merecer esta adición.
Sin embargo, me pregunto qué ideas inteligentes se nos ocurren.
fuente
using single letters in global namespace will probably be considered bad practice
Pero eso no tiene relevancia: (A) Los UDL deben definirse en el ámbito del espacio de nombres (no global) ... presumiblemente porque (B) deben consistir en un guión bajo y luego> = 1 letra, no solo la letra, y dichos identificadores en los NS globales están reservados para la implementación. Eso es al menos 2 puntos en contra de la idea de que los UDL generan confusión de forma innata. En cuanto a tener que abarcar el espacio de nombres que reduce la utilidad de la función, es por eso que stdlib los declara eninline namespace
s que los usuarios pueden importar al por mayor si lo desean.Usé literales de usuario para cadenas binarias como esta:
utilizando
std::string(str, n)
constructor para que\0
no corte la cadena por 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 contenedor parastd::vector
.fuente
El ruido de línea en esa cosa es enorme. También es horrible de leer.
Déjame saber, ¿razonaron esa nueva adición de sintaxis con algún tipo de ejemplos? Por ejemplo, ¿tienen un par de programas que ya usan C ++ 0x?
Para mí, esta parte:
No justifica esta parte:
Ni siquiera si usarías la sintaxis i 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 probablemente escribirás sobre todo en todas partes esto:
Sin embargo, la palabra clave 'auto' puede estar justificada, solo tal vez. Pero tomemos solo C ++, porque es mejor que C ++ 0x en este aspecto.
Es como ... así de simple. Incluso aunque todos los soportes estándar y puntiagudos sean poco convincentes si los usas en todas partes. No empiezo a adivinar qué sintaxis hay en C ++ 0x para convertir std :: complex en complex.
Tal vez sea algo sencillo, pero no creo que sea tan simple en C ++ 0x.
¿Quizás? > :)
De todos modos, el punto es: escribir 3.14i en lugar de std :: complex (0, 3.14); en general no le ahorra mucho tiempo, excepto en algunos casos súper especiales.
fuente
std::complex<double> val(0, 3.14);
.