teclado sirven signos signo que puntuacion programacion para matematicas los llaves ejemplos corchetes comillas c++ templates syntax d

sirven - ¿Cuáles son todos los problemas de sintaxis introducidos por el uso de corchetes angulares en las plantillas de C++?



signos de programacion c++ (8)

En C ++, las plantillas están instanciadas con corchetes angulares vector<int> y los lenguajes Java y C # han adoptado la misma sintaxis para sus genéricos.

Sin embargo, los creadores de D han expresado sus opiniones sobre los problemas que traen los corchetes angulares y crearon una nueva sintaxis foo!(int) , pero nunca he visto demasiados detalles sobre qué problemas traen los corchetes angulares, exactamente.

Uno de ellos fue al crear una instancia de una plantilla con otro vector<vector<int>> plantilla vector<vector<int>> , lo que causaría que algunos compiladores (¿más antiguos?) Confundieran el ''>> `final con los operadores de cambio de bits o de transmisión. La solución fue insertar un espacio entre los dos corchetes angulares, pero ¿los compiladores no han podido analizar esa sintaxis en la actualidad?

Otro problema fue cuando se usa el operador mayor que foo<3 > 2> . El analizador pensaría que el operador realmente cierra la creación de instancias de la plantilla; la solución fue introducir paréntesis foo<(3 > 2)> . Pero no creo que haya muchos casos en los que necesite hacer esto y, en cualquier caso, prefiero escribir los paréntesis adicionales cuando sean necesarios, en lugar de introducir una nueva sintaxis y tener que escribir siempre el signo de exclamación .

¿Qué otros problemas hay entre paréntesis angulares que hicieron que los desarrolladores de D crearan una nueva sintaxis?


¿Pero los compiladores no han podido analizar esa sintaxis hoy en día?

Por supuesto. Pero está lejos de ser trivial. En particular, le impide implementar una separación limpia entre el analizador y el analizador que no conocen el contexto. Esto es particularmente molesto para los resaltadores de sintaxis y otras herramientas de soporte que necesitan analizar C ++, pero no quieren / pueden implementar un analizador sintáctico completo.

Hace que C ++ sea mucho más difícil de analizar que muchas herramientas simplemente no molestarán. Esta es una pérdida neta para el ecosistema. Dicho de otra manera: hace que el desarrollo de una herramienta de análisis sea mucho más costoso.

Por ejemplo, ctags falla para algunas definiciones de plantillas, lo que lo hace inutilizable con nuestro proyecto actual de C ++. Muy molesto.

Pero no creo que haya muchos casos en los que necesite [distinguir entre paréntesis angulares y menos de]

No importa con qué frecuencia necesites hacer esto. Tu analizador aún necesita manejar esto.

La decisión de D de dejar caer backets de ángulo fue una obviedad. Cualquier razón hubiera sido suficiente, dado que es un beneficio neto.


Creo que esos fueron los únicos casos.

Sin embargo, no es tanto un problema de usuario como un problema de implementador . Esta diferencia aparentemente trivial hace que sea mucho más difícil crear un analizador correcto para C ++ (en comparación con D). D también fue diseñado para ser amigable con los implementadores, y como tal hicieron todo lo posible para evitar hacer posible el código ambiguo.

(Nota: creo que la combinación del punto de exclamación de cambio es un poco incómoda ... ¡una de las ventajas de los corchetes angulares es sin duda la facilidad para escribir!)


Diviértete descubriendo lo que hace esto:

bool b = A< B>::C == D<E >::F(); bool b = A<B>::C == D<E>::F();

La última vez que lo verifiqué, podrías analizarlo de cualquier manera cambiando el alcance.

Usar < y > como tokens coincidentes y no coincidentes es un desastre. En cuanto a !() prolonga el uso de D: para el caso común de tener un solo argumento, los () son opcionales, por ejemplo, esto es legal:

Set!int foo;


El problema es hacer que la gramática del lenguaje esté libre de contexto. Cuando un programa es tokenizado por el lexer, utiliza una técnica llamada munch máxima , lo que significa que siempre toma la cadena más larga posible que podría designar un token. Eso significa que >> se trata como el operador de desplazamiento de bits correcto. Por lo tanto, si tiene algo como vector<pair<int, int>> , el >> en el extremo se trata como el operador de desplazamiento de bits correcto en lugar de como parte de una creación de instancias de plantilla. Para que >> sea ​​tratado de manera diferente en este contexto, debe ser sensible al contexto en lugar de libre del contexto, es decir, tiene que preocuparse realmente por el contexto de los tokens que se analizan. Esto complica considerablemente el lexer y el analizador. Cuanto más complicados son el lexer y el analizador, mayor es el riesgo de errores y, lo que es más importante, más difícil es que las herramientas los implementen, lo que significa menos herramientas. Cuando el resaltado de sintaxis en un IDE o editor de código se complica de implementar, es un problema.

Al usar !() , Que resultaría en vector!(pair!(int, int)) para la misma declaración, D evita el problema de sensibilidad al contexto. D ha hecho una serie de tales elecciones en su gramática explícitamente con la idea de facilitar que las herramientas implementen el lexing o el análisis cuando lo necesitan para hacer lo que hacen. Y ya que realmente no hay inconveniente en usar !() Para otras plantillas que no sea el hecho de que es un poco extraño para los programadores que han usado plantillas o genéricos en otros idiomas que usan <> , es una opción de diseño de lenguaje.

Y la frecuencia con la que utiliza o no las plantillas que crearían ambigüedades al utilizar la sintaxis del corchete angular, por ejemplo, vector<pair<int, int>> , no es realmente relevante para el idioma. Las herramientas deben implementarlo independientemente. La decisión de usar !() lugar de <> es una cuestión de simplificar el lenguaje para las herramientas, no para el programador. Y si bien puede que le guste o no la sintaxis !() , Es bastante fácil de usar, por lo que en última instancia no causa a los programadores ningún problema más allá de su aprendizaje y el hecho de que puede ir en contra de sus preferencias personales.


En última instancia, lo que cualquier compilador tiene que hacer es traducir su código fuente semi-inglés, en cualquier idioma, al código de máquina real en el que realmente puede operar una computadora. Esto es, en última instancia, una serie de TRANSFORMES matemáticos increíblemente complejos.

Bueno, las matemáticas nos dicen que los mapas que necesitamos para la compilación son "on" o "surjective". Todo lo que significa es que cada programa legal PUEDE asignarse de forma inequívoca al ensamblaje. Esto es lo que las palabras clave y la puntuación del lenguaje tienen gusto de ";" Existen para, y por qué cada idioma los tiene. Sin embargo, lenguajes como C ++ usan los mismos símbolos como "{}" y "<>" para varias cosas, por lo que el compilador tiene que agregar pasos adicionales para producir la transformación global que necesita (esto es lo que está haciendo en lineal álgebra cuando multiplicas matrices). Eso se suma a los tiempos de compilación, introduce una complejidad significativa que puede contener errores y puede limitar la capacidad del compilador para optimizar la salida.

Por ejemplo, Strousoup podría haber usado ''@'' para el argumento de las plantillas, era un carácter no utilizado que hubiera sido perfecto para que los compiladores sepan que "esto es, y solo lo será, algún tipo de plantilla". Eso es en realidad una transformación 1 a 1, que es perfecta para las herramientas analíticas. Pero no lo hizo; usó símbolos que ya estaban mapeados a mayor que y menor que. Eso solo introduce de inmediato la ambigüedad, y solo empeora a partir de ahí.

Parece que "D" decidió convertir la secuencia ''! ()'' En un símbolo especial, que se usa solo para las plantillas, como mi ejemplo ''@'' anterior. Estoy dispuesto a adivinar que su código altamente templado compila más rápido y con menos errores como resultado.


En C ++, otro problema es que el preprocesador no entiende los paréntesis angulares, por lo que esto falla:

#define FOO(X) typename something<X>::type FOO(std::map<int, int>)

El problema es que el preprocesador cree que se está llamando a FOO con dos argumentos: std::map<int e int> . Este es un ejemplo del problema más amplio, que a menudo es ambiguo si el símbolo es un operador o un soporte.


Personalmente, el problema más horrible que he visto es la invocación de funciones de plantilla en el contexto dependiente:

template <typename T> void foo(T t) { t.bar<3>(); }

Esto parece ciertamente simple, pero de hecho es incorrecto. El estándar de C ++ requiere la introducción de la palabra clave de la template para desambiguar t.bar < 3 frente a una invocación de método que produce:

t.template bar<3>(); // iirk

litb hizo algunos posts muy interesantes con respecto a la posible interpretación que podría tener un compilador.

Con respecto a la >> cuestión, se corrigió en C ++ 0x, pero requiere compiladores más inteligentes.