Parênteses em C ++ são usados em muitos lugares: por exemplo, em chamadas de função e expressões de agrupamento para substituir a precedência do operador. Além de parênteses extras ilegais (como em torno de listas de argumentos de chamadas de função), uma regra geral -mas não absoluta- de C ++ é que parênteses extras nunca fazem mal :
5.1 Expressões primárias [expr.prim]
5.1.1 Geral [expr.prim.general]
6 Uma expressão entre parênteses é uma expressão primária cujo tipo e valor são idênticos aos da expressão incluída. A presença de parênteses não afeta se a expressão é um lvalue. A expressão entre parênteses pode ser usada exatamente nos mesmos contextos em que a expressão incluída pode ser usada, e com o mesmo significado, exceto quando indicado de outra forma .
Pergunta : em quais contextos os parênteses extras mudam o significado de um programa C ++, exceto sobrepor a precedência do operador básico?
NOTA : Eu considero a restrição da sintaxe de ponteiro para membro&qualified-id
sem parênteses fora do escopo porque restringe a sintaxe em vez de permitir duas sintaxes com significados diferentes. Da mesma forma, o uso de parênteses dentro das definições de macro do pré-processador também protege contra precedência de operador indesejada.
fonte
&(C::f)
, o operando de&
permaneceC::f
, não é?expr.unary.op/4
: Um ponteiro para membro é formado apenas quando um explícito&
é usado e seu operando é um id qualificado não colocado entre parênteses.()
sobre o seletor de ponteiro para membro::*
Respostas:
TL; DR
Parênteses extras mudam o significado de um programa C ++ nos seguintes contextos:
decltype
expressõesEvitando a procura de nome dependente de argumento
Conforme detalhado no Anexo A da Norma, a
post-fix expression
do formulário(expression)
é umprimary expression
, mas não umid-expression
e, portanto, não é umunqualified-id
. Isso significa que a pesquisa de nome dependente de argumento é evitada em chamadas de função do formulário em(fun)(arg)
comparação com o formato convencionalfun(arg)
.3.4.2 Pesquisa de nome dependente de argumento [basic.lookup.argdep]
Ativando o operador vírgula em contextos de lista
O operador vírgula tem um significado especial na maioria dos contextos do tipo lista (argumentos de função e modelo, listas de inicializadores, etc.). Parênteses do formulário
a, (b, c), d
em tais contextos podem habilitar o operador vírgula em comparação com o formulário regulara, b, c, d
onde o operador vírgula não se aplica.5.18 Operador vírgula [expr.comma]
Resolução de ambiguidade de análises incômodas
A compatibilidade com versões anteriores com C e sua sintaxe de declaração de função misteriosa pode levar a surpreendentes ambigüidades de análise, conhecidas como análises complicadas. Essencialmente, qualquer coisa que possa ser analisada como uma declaração será analisada como uma , mesmo que uma análise concorrente também se aplique.
6.8 Resolução de ambigüidade [stmt.ambig]
8.2 Resolução de ambigüidade [dcl.ambig.res]
Um exemplo famoso disso é o mais irritante Parse , um nome popularizado por Scott Meyers no item 6 de seu livro Effective STL :
Isso declara uma função,,
data
cujo tipo de retorno élist<int>
. Os dados da função têm dois parâmetros:dataFile
. Seu tipo éistream_iterator<int>
. Os parênteses ao redordataFile
são supérfluos e são ignorados.istream_iterator<int>
.Colocar parênteses extras ao redor do primeiro argumento da função (parênteses ao redor do segundo argumento são ilegais) irá resolver a ambigüidade
C ++ 11 tem sintaxe de inicializador de chave que permite contornar tais problemas de análise em muitos contextos.
Deduzindo referencia em
decltype
expressõesEm contraste com a
auto
dedução de tipo,decltype
permite que a referência (referências lvalue e rvalue) seja deduzida. As regras distinguem entre expressõesdecltype(e)
edecltype((e))
:7.1.6.2 Especificadores de tipo simples [dcl.type.simple]
As regras para
decltype(auto)
têm um significado semelhante para parênteses extras no RHS da expressão de inicialização. Aqui está um exemplo do FAQ de C ++ e este Q&A relacionadoO primeiro retorna
string
, o segundo retornastring &
, que é uma referência à variável localstr
.Prevenção de erros relacionados à macro do pré-processador
Há uma série de sutilezas com macros de pré-processador em sua interação com a linguagem C ++ adequada, as mais comuns das quais estão listadas abaixo
#define TIMES(A, B) (A) * (B);
para evitar a precedência indesejada do operador (por exemplo, emTIMES(1 + 2, 2 + 1)
que resulta em 9, mas resultaria em 6 sem os parênteses em volta(A)
e(B)
assert((std::is_same<int, int>::value));
que de outra forma não seriam compilados(min)(a, b)
(com o efeito colateral indesejado de também desativar o ADL)fonte
if
/while
se a expressão for uma atribuição. Por exemploif (a = b)
- aviso (você quis dizer==
?), Enquantoif ((a = b))
- nenhum aviso.(min)(a, b)
(com o mal MACROmin(A, B)
) é parte da prevenção nome lookup dependente de argumento?Em geral, em linguagens de programação, parênteses "extras" implicam que eles não estão mudando a ordem ou o significado da análise sintática. Eles estão sendo adicionados para esclarecer a ordem (precedência do operador) para o benefício das pessoas que lêem o código, e seu único efeito seria desacelerar um pouco o processo de compilação e reduzir os erros humanos na compreensão do código (provavelmente acelerando o processo geral de desenvolvimento )
Se um conjunto de parênteses realmente muda a maneira como uma expressão é analisada, eles, por definição, não são extras. Parênteses que transformam uma análise ilegal / inválida em legal não são "extras", embora isso possa indicar um design de linguagem pobre.
fonte