Como funciona o Operador Vírgula
-
09-06-2019 - |
Pergunta
Como funciona o operador vírgula em C++?
Por exemplo, se eu fizer:
a = b, c;
A acaba sendo igual a b ou c?
(Sim, eu sei que isso é fácil de testar - basta documentar aqui para que alguém encontre a resposta rapidamente.)
Atualizar: Esta questão expôs uma nuance ao usar o operador vírgula.Apenas para documentar isso:
a = b, c; // a is set to the value of b!
a = (b, c); // a is set to the value of c!
Na verdade, esta pergunta foi inspirada por um erro de digitação no código.O que pretendia ser
a = b;
c = d;
Se tornou
a = b, // <- Note comma typo!
c = d;
Solução
Seria igual a b
.
O operador vírgula tem uma precedência menor que a atribuição.
Outras dicas
Tome cuidado para observar que o operador vírgula pode estar sobrecarregado em C++.O comportamento real pode, portanto, ser muito diferente do esperado.
Como um exemplo, Impulsionar.Espírito usa o operador vírgula de maneira bastante inteligente para implementar inicializadores de lista para tabelas de símbolos.Assim, torna a seguinte sintaxe possível e significativa:
keywords = "and", "or", "not", "xor";
Observe que devido à precedência do operador, o código é (intencionalmente!) idêntico ao
(((keywords = "and"), "or"), "not"), "xor";
Ou seja, o primeiro operador chamado é keywords.operator =("and")
que retorna um objeto proxy no qual o restante operator,
s são invocados:
keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");
O operador vírgula tem o mais baixo precedência de todos os operadores C/C++.Portanto, é sempre o último a ser vinculado a uma expressão, ou seja:
a = b, c;
é equivalente a:
(a = b), c;
Outro fato interessante é que o operador vírgula introduz um ponto de sequência.Isso significa que a expressão:
a+b, c(), d
é garantido que terá suas três subexpressões (a+b, c() e d) avaliados em ordem.Isto é significativo se eles tiverem efeitos colaterais.Normalmente, os compiladores podem avaliar subexpressões na ordem que acharem adequada;por exemplo, em uma chamada de função:
someFunc(arg1, arg2, arg3)
os argumentos podem ser avaliados em uma ordem arbitrária.Observe que as vírgulas na chamada de função são não operadores;eles são separadores.
O operador vírgula:
- tem a menor precedência
- é associativo à esquerda
Uma versão padrão do operador vírgula é definida para todos os tipos (integrados e personalizados) e funciona da seguinte maneira - fornecido exprA , exprB
:
exprA
é avaliado- o resultado de
exprA
é ignorado exprB
é avaliado- o resultado de
exprB
é retornado como o resultado de toda a expressão
Com a maioria dos operadores, o compilador pode escolher a ordem de execução e é até obrigado a pular a execução se isso não afetar o resultado final (por exemplo, false && foo()
vai pular a chamada para foo
).No entanto, este não é o caso do operador vírgula e as etapas acima sempre acontecerão*.
Na prática, o operador vírgula padrão funciona quase da mesma maneira que um ponto e vírgula.A diferença é que duas expressões separadas por ponto e vírgula formam duas instruções separadas, enquanto a separação por vírgula mantém tudo como uma única expressão.É por isso que o operador vírgula às vezes é usado nos seguintes cenários:
- A sintaxe C requer um único expressão, não uma declaração.por exemplo.em
if( HERE )
- A sintaxe C requer uma única instrução, não mais, por ex.na inicialização do
for
laçofor ( HERE ; ; )
- Quando você quiser pular chaves e manter uma única instrução:
if (foo) HERE ;
(por favor, não faça isso, é muito feio!)
Quando uma instrução não é uma expressão, o ponto e vírgula não pode ser substituído por vírgula.Por exemplo, estes não são permitidos:
(foo, if (foo) bar)
(if
não é uma expressão)- int x, int y (declaração de variável não é uma expressão)
No seu caso temos:
a=b, c;
, equivalente aa=b; c;
, assumindo quea
é do tipo que não sobrecarrega o operador vírgula.a = b, c = d;
equivalente aa=b; c=d;
, assumindo quea
é do tipo que não sobrecarrega o operador vírgula.
Observe que nem toda vírgula é na verdade um operador de vírgula.Algumas vírgulas que têm um significado completamente diferente:
int a, b;
--- a lista de declarações de variáveis é separada por vírgula, mas não são operadores de vírgulaint a=5, b=3;
--- esta também é uma lista de declaração de variáveis separada por vírgulafoo(x,y)
--- lista de argumentos separados por vírgula.Na verdade,x
ey
pode ser avaliado em qualquer ordem!FOO(x,y)
--- lista de argumentos de macro separados por vírgulafoo<a,b>
--- lista de argumentos de modelo separados por vírgulaint foo(int a, int b)
--- lista de parâmetros separados por vírgulaFoo::Foo() : a(5), b(3) {}
--- lista de inicializadores separados por vírgula em um construtor de classe
* Isso não é totalmente verdade se você aplicar otimizações.Se o compilador reconhecer que determinado trecho de código não tem absolutamente nenhum impacto sobre o restante, ele removerá as instruções desnecessárias.
Leitura adicional: http://en.wikipedia.org/wiki/Comma_operator
O valor de a
vai ser b
, mas o valor de a expressão vai ser c
.Que está em
d = (a = b, c);
a seria igual a b
, e d
seria igual a c
.
O valor de b será atribuído a a.Nada vai acontecer com c
O valor de a será igual a b, pois o operador vírgula tem precedência menor que o operador de atribuição.
Sim, o operador Vírgula tem precedência baixa que o operador Atribuição
#include<stdio.h>
int main()
{
int i;
i = (1,2,3);
printf("i:%d\n",i);
return 0;
}
Saída :eu=3
Porque o operador vírgula sempre retorna o valor mais à direita.
No caso de operador vírgula com Operador de Atribuição:
int main()
{
int i;
i = 1,2,3;
printf("i:%d\n",i);
return 0;
}
Saída:eu=1
Como sabemos, o operador vírgula tem precedência menor que a atribuição.....
Primeiras coisas primeiro: Na verdade, a vírgula não é um operador, para o compilador é apenas um token que ganha um significado no contexto com outros tokens.
O que isso significa e por que se preocupar?
Exemplo 1:
Para entender a diferença entre o significado do mesmo token em um contexto diferente, damos uma olhada neste exemplo:
class Example {
Foo<int, char*> ContentA;
}
Normalmente, um iniciante em C++ pensaria que esta expressão poderia/iria comparar coisas, mas é absolutamente errada, o significado do <
, >
e ,
os tokens dependem do contexto de uso.
A interpretação correta do exemplo acima é, obviamente, que se trata de uma instauração de um modelo.
Exemplo 2:
Quando escrevemos um loop for típico com mais de uma variável de inicialização e/ou mais de uma expressão que deve ser feita após cada iteração do loop, usamos vírgula também:
for(a=5,b=0;a<42;a++,b--)
...
O significado da vírgula depende do contexto de uso, aqui é o contexto da for
construção.
O que realmente significa uma vírgula no contexto?
Para complicar ainda mais (como sempre em C++), o próprio operador vírgula pode estar sobrecarregado (graças a Konrad Rodolfo por apontar isso).
Voltando à questão, o Código
a = b, c;
significa para o compilador algo como
(a = b), c;
porque o prioridade do =
token/operador é maior que a prioridade do ,
símbolo.
e isso é interpretado no contexto como
a = b;
c;
(observe que a interpretação depende do contexto, aqui não é uma chamada de função/método ou uma instanciação de modelo.)