¿Alguien me puede explicar por qué tener 2 operadores de concatenación no produce ningún error por parte de un preprocesador?:
#define Z(x) x ## ## 3 Z(3)resultados en:
33Las normas dicen que:
... cada instancia de un token de preprocesamiento ## en la lista de reemplazo (no de un argumento) se elimina y el token de preprocesamiento anterior se concatena con el siguiente token de preprocesamiento
así que esperaría que el preprocesador primero intente concatenar x con el segundo ## , lo que parece extraño. Esto no produce ningún token válido, por lo que esperaría al menos una advertencia. Ni gcc ni VC producen ninguna advertencia.
Agradecería alguna explicación de cómo funciona esto y por qué. El estándar menciona tokens temporales de placemaker que explicarían por qué esto funciona, pero tendría que haber uno de esos tokens entre ambos 'dobles objetos punzantes'. El problema es que los tokens placemaker se generan cuando el parámetro no contiene tokens y no hay ningún parámetro entre ambos operadores de concatenación.
(Los estándares C y C++ tienen una redacción esencialmente idéntica en §6.10.3.3 y §16.3.3 respectivamente. En caso de que haya diferencias menores, tomé las citas de C11).
No se especifica explícitamente el orden en que se procesan los operadores ## : ("No se especifica el orden de evaluación de los operadores ##", última frase del párrafo 3; véase también el párrafo 2 de la sección anterior). Por lo tanto, no puede decir que el "preprocesador primero intenta concatenar x con el segundo ##"; primero podría intentar concatenar el primer ## con el 3 . Eso tampoco produciría un token válido, por lo que es un poco problemático. Pero es importante recordar.
La pregunta es si la declaración de que el orden de evaluación no se especifica permite que la evaluación sea intercalada . En otras palabras, ¿podría un preprocesador satisfacer el estándar eliminando primero el segundo ## , luego eliminando el primero y finalmente produciendo una sola concatenación? Ciertamente, en el modelo de ejecución, está claro que las operaciones no secuenciadas pueden intercalarse. (Ver nota 13 en §5.1.2.3. En C++, las palabras son "puede superponerse"; §1.9/13)
Eso podría ser un poco exagerado, pero también vale la pena señalar que después de la concatenación:
Si el resultado no es un token de preprocesamiento válido, el comportamiento no está definido.
Esto no está marcado como una restricción, por lo que no se requiere un mensaje de error. Y dado que el comportamiento indefinido libera al compilador de cualquier obligación con el estándar, supongo que gcc tiene todo el derecho de producir el comportamiento observado.
En resumen, la cadena de reemplazo de macro proporcionada en la pregunta original implica un comportamiento no especificado o indefinido, pero no una violación de restricción. En consecuencia, el compilador no tiene la obligación de producir un diagnóstico.
No producir un diagnóstico en este caso podría considerarse un problema de calidad de implementación. Por otro lado, no conozco ningún compilador que produzca una advertencia para macros con un orden de evaluación ## ambiguo. Aparte de la restricción de que una lista de expansión de macros no puede comenzar o terminar con un token ## , que debe ser diagnosticado por el compilador, es responsabilidad total del programador asegurarse de que las expresiones de concatenación estén bien definidas.