Los paréntesis en C ++ se utilizan en muchos lugares: por ejemplo, en llamadas a funciones y expresiones de agrupación para anular la precedencia de los operadores. Aparte de los paréntesis adicionales ilegales (como alrededor de las listas de argumentos de llamadas a funciones), una regla general, pero no absoluta, de C ++ es que los paréntesis adicionales nunca hacen daño :
5.1 Expresiones primarias [expr.prim]
5.1.1 General [expr.prim.general]
6 Una expresión entre paréntesis es una expresión primaria cuyo tipo y valor son idénticos a los de la expresión incluida. La presencia de paréntesis no afecta si la expresión es un valor l. La expresión entre paréntesis se puede utilizar exactamente en los mismos contextos en los que se puede utilizar la expresión entre paréntesis, y con el mismo significado, salvo que se indique lo contrario .
Pregunta : ¿en qué contextos los paréntesis adicionales cambian el significado de un programa C ++, además de anular la precedencia básica del operador?
NOTA : Considero que la restricción de la sintaxis de puntero a miembro&qualified-id
sin paréntesis está fuera del alcance porque restringe la sintaxis en lugar de permitir dos sintaxis con diferentes significados. De manera similar, el uso de paréntesis dentro de las definiciones de macros del preprocesador también protege contra la precedencia de operadores no deseada.
fuente
&(C::f)
, el operando de&
sigue siendoC::f
, ¿no es así?expr.unary.op/4
: un puntero a miembro solo se forma cuando&
se usa un explícito y su operando es un id-calificado que no está entre paréntesis.()
sobre el selector de puntero a miembro::*
Respuestas:
TL; DR
Los paréntesis adicionales cambian el significado de un programa C ++ en los siguientes contextos:
decltype
expresionesEvitar la búsqueda de nombres dependiente de argumentos
Como se detalla en el Anexo A de la Norma, un
post-fix expression
del formulario(expression)
es unprimary expression
, pero no unid-expression
, y por lo tanto no ununqualified-id
. Esto significa que la búsqueda de nombres dependientes de argumentos se evita en las llamadas a funciones de la forma en(fun)(arg)
comparación con la forma convencionalfun(arg)
.3.4.2 Búsqueda de nombre dependiente del argumento [basic.lookup.argdep]
Habilitar el operador de coma en contextos de lista
El operador de coma tiene un significado especial en la mayoría de los contextos similares a listas (argumentos de función y plantilla, listas de inicializadores, etc.). Los paréntesis del formulario
a, (b, c), d
en tales contextos pueden habilitar el operador de coma en comparación con el formulario regulara, b, c, d
donde no se aplica el operador de coma.5.18 Operador de coma [expr.comma]
Resolución de ambigüedad de análisis molestos
La compatibilidad con versiones anteriores de C y su sintaxis de declaración de función arcana puede conducir a sorprendentes ambigüedades de análisis, conocidas como análisis molestos. Básicamente, todo lo que se pueda analizar como una declaración se analizará como uno , aunque también se aplicaría un análisis de la competencia.
6.8 Resolución de ambigüedad [stmt.ambig]
8.2 Resolución de ambigüedad [dcl.ambig.res]
Un ejemplo famoso de esto es Most Vexing Parse , un nombre popularizado por Scott Meyers en el artículo 6 de su libro Effective STL :
Esto declara una función
data
, cuyo tipo de retorno eslist<int>
. Los datos de la función toman dos parámetros:dataFile
. Su tipo esistream_iterator<int>
. Los paréntesis alrededordataFile
son superfluos y se ignoran.istream_iterator<int>
.Colocar paréntesis adicionales alrededor del primer argumento de la función (los paréntesis alrededor del segundo argumento son ilegales) resolverá la ambigüedad
C ++ 11 tiene una sintaxis de inicializador de llaves que permite evitar estos problemas de análisis en muchos contextos.
Deducir referencias en
decltype
expresionesA diferencia de la
auto
deducción de tipo,decltype
permite deducir la referencia (referencias lvalue y rvalue). Las reglas distinguen entre expresionesdecltype(e)
ydecltype((e))
:7.1.6.2 Especificadores de tipo simple [dcl.type.simple]
Las reglas para
decltype(auto)
tienen un significado similar para paréntesis adicionales en el RHS de la expresión de inicialización. Aquí hay un ejemplo de las preguntas frecuentes de C ++ y estas preguntas y respuestas relacionadasEl primero devuelve
string
, el segundo devuelvestring &
, que es una referencia a la variable localstr
.Prevención de errores relacionados con la macro del preprocesador
Existe una gran cantidad de sutilezas con las macros de preprocesador en su interacción con el lenguaje C ++ propiamente dicho, las más comunes de las cuales se enumeran a continuación.
#define TIMES(A, B) (A) * (B);
para evitar la precedencia de operadores no deseados (por ejemplo, en elTIMES(1 + 2, 2 + 1)
que produce 9 pero produciría 6 sin los paréntesis alrededor(A)
y(B)
assert((std::is_same<int, int>::value));
que de otra manera no se compilarían(min)(a, b)
(con el efecto secundario no deseado de deshabilitar también ADL)fuente
if
/while
si la expresión es una asignación. Por ejemploif (a = b)
: advertencia (¿quiso decir==
?), Mientras queif ((a = b))
: sin advertencia.(min)(a, b)
(With evil MACROmin(A, B)
) es parte de la prevención de búsqueda de nombres dependiente de argumentos?En general, en los lenguajes de programación, los paréntesis "extra" implica que están no cambiando el orden de análisis sintáctico o significado. Se están agregando para aclarar el orden (precedencia del operador) en beneficio de las personas que leen el código, y su único efecto sería ralentizar ligeramente el proceso de compilación y reducir los errores humanos en la comprensión del código (probablemente acelerando el proceso de desarrollo general ).
Si un conjunto de paréntesis cambia realmente la forma en que se analiza una expresión, entonces, por definición, no son adicionales. Los paréntesis que convierten un análisis ilegal / inválido en uno legal no son "extra", aunque eso puede indicar un diseño de lenguaje deficiente.
fuente