C++ assert: la precedencia de la expresión en una macro assert
c-preprocessor operator-precedence (2)
En C ++:
assert( std::is_same<int , int>::value ); // does not compile
assert( (std::is_same<int , int>::value) ); // compiles
¿Alguien puede explicar por qué?
La coma se trata como un separador de argumentos para la macro, pero el paréntesis en su segundo caso protege los argumentos. Podemos ver esto yendo al borrador de la sección 16.3
estándar de C ++. Reemplazo de macro que dice ( énfasis mío ):
La secuencia de tokens de preprocesamiento delimitados por los paréntesis coincidentes más externos forma la lista de argumentos para la macro similar a una función. Los argumentos individuales dentro de la lista están separados por tokens de preprocesamiento de coma, pero los tokens de preprocesamiento de coma entre paréntesis internos no separan los argumentos. Si hay secuencias de tokens de preprocesamiento dentro de la lista de argumentos que de lo contrario actuarían como directivas de preprocesamiento, 154 el comportamiento es indefinido
Podemos ver que la expansión de macros ocurre antes del análisis semántico yendo a la sección 2.2
Fases de la traducción y vemos que la fase 4 incluye:
Las directivas de preprocesamiento se ejecutan, las invocaciones de macros se expanden y, a continuación, se eliminan todas las directivas de preprocesamiento.
y la fase 7 incluye:
[...] Cada token de preprocesamiento se convierte en un token. (2.7). Los tokens resultantes se analizan sintácticamente y semánticamente y se traducen como una unidad de traducción [...]
Como nota al margen, podemos ver que Boost incluye una macro especial para hacer frente a esta situación: BOOST_PP_COMMA :
La macro BOOST_PP_COMMA se expande a una coma.
y dice:
El preprocesador interpreta comas como separadores de argumento en invocaciones de macro. Debido a esto, las comas requieren un manejo especial.
y un ejemplo:
BOOST_PP_IF(1, BOOST_PP_COMMA, BOOST_PP_EMPTY)() // expands to ,
assert
es una macro preprocesadora. Las macros del preprocesador son tontas; no entienden las plantillas El preprocesador ve 10 tokens dentro de los paréntesis:
assert( std :: is_same < int , int > :: value );
Se divide en la coma. No sabe que este es el lugar equivocado para dividirse, porque no entiende que std::is_same<int
and int>::value
no son expresiones válidas de C ++.
El preprocesador es lo suficientemente inteligente como para no dividir el contenido de pares internos de paréntesis a través de múltiples argumentos. Es por eso que agregar los paréntesis adicionales soluciona el problema.