Como o operador de vírgula funciona em C ++?
Por exemplo, se eu fizer:
a = b, c;
A acaba igualando b ou c?
(Sim, eu sei que isso é fácil de testar - basta documentar aqui para alguém encontrar a resposta rapidamente.)
Atualização: Esta pergunta expôs uma nuance ao usar o operador de 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!
Esta questão foi realmente inspirada por um erro de digitação no código. O que era para ser
a = b;
c = d;
Se tornou
a = b, // <- Note comma typo!
c = d;
c++
comma-operator
Joe Schneider
fonte
fonte
a = (b, c);
.a = b, c = d;
na verdade, executa o mesmo que o pretendidoa = b; c = d;
?b
ed
são avaliações de função que usam (e modificam) um estado comum, a ordem de execução não é definida atéC++17
.Respostas:
Seria igual a
b
.O operador de vírgula tem uma precedência mais baixa que a atribuição.
fonte
Observe que o operador de vírgula pode estar sobrecarregado em C ++. O comportamento real pode, portanto, ser muito diferente do esperado.
Como exemplo, o Boost.Spirit usa o operador vírgula de maneira inteligente para implementar inicializadores de lista para tabelas de símbolos. Assim, torna a seguinte sintaxe possível e significativa:
Observe que, devido à precedência do operador, o código é (intencionalmente!) Idêntico ao
Ou seja, o primeiro operador chamado é o
keywords.operator =("and")
que retorna um objeto proxy no qual osoperator,
s restantes são chamados:fonte
char[]
, que não podem ser sobrecarregados. O código intencionalmente primeiro chama ooperator=
e, posteriormente,operator,
para cada elemento restante.O operador de vírgula tem a menor precedência de todos os operadores C / C ++. Portanto, é sempre o último a vincular a uma expressão, o que significa:
é equivalente a:
Outro fato interessante é que o operador de vírgula introduz um ponto de sequência . Isso significa que a expressão:
é garantido que suas três subexpressões ( a + b , c () e d ) sejam avaliadas em ordem. Isso é significativo se eles tiverem efeitos colaterais. Normalmente, os compiladores têm permissão para avaliar subexpressões na ordem que acharem adequada; por exemplo, em uma chamada de função:
argumentos podem ser avaliados em uma ordem arbitrária. Observe que as vírgulas na chamada de função não são operadores; eles são separadores.
fonte
,
essa precedência é tão baixa que fica atrás de si mesma ;) ... Ou seja: vírgula como operador tem uma precedência menor que a vírgula como separadora . Portanto, se você quiser usar vírgula como operador em um argumento de função única, atribuição de variável ou outra lista separada por vírgula - precisará usar parênteses, por exemplo:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
O operador de vírgula:
Uma versão padrão do operador de vírgula é definida para todos os tipos (interno e personalizado) e funciona da seguinte maneira
exprA , exprB
:exprA
é avaliadoexprA
é ignoradoexprB
é avaliadoexprB
é retornado como resultado de toda a expressãoCom a maioria dos operadores, o compilador pode escolher a ordem de execução e é necessário pular a execução, se não afetar o resultado final (por exemplo
false && foo()
, a chamada parafoo
). No entanto, esse não é o caso do operador de vírgula e as etapas acima sempre acontecerão * .Na prática, o operador de 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 declarações separadas, enquanto a separação por vírgula mantém tudo como uma expressão única. É por isso que o operador de vírgula às vezes é usado nos seguintes cenários:
if( HERE )
for
loopfor ( HERE ; ; )
if (foo) HERE ;
(por favor, não faça isso, é realmente feio!)Quando uma instrução não é uma expressão, o ponto e vírgula não pode ser substituído por uma vírgula. Por exemplo, estes não são permitidos:
(foo, if (foo) bar)
(if
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 de vírgula.a = b, c = d;
equivalente aa=b; c=d;
, supondo quea
seja do tipo que não sobrecarregue o operador de vírgula.Observe que nem toda vírgula é realmente um operador de vírgula. Algumas vírgulas que têm um significado completamente diferente:
int a, b;
--- lista de declaração variável é separada por vírgula, mas estes não são operadores de vírgulaint a=5, b=3;
--- esta também é uma lista de declarações de variáveis separadas por vírgulafoo(x,y)
--- lista de argumentos separados por vírgula. De fato,x
ey
pode ser avaliado em qualquer ordem!FOO(x,y)
--- lista de argumentos de macro separada 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
fonte
operator ,
sobrecarga for perdida, você perderá qualquer garantia de associatividade (assim como as propriedades de curto-circuito daoperator&&
eoperator||
se elas estiverem sobrecarregadas)?a, b, c
sempre significa(a, b), c
e nuncaa, (b, c)
. A última interpretação pode até levar a erros de compilação se os elementos forem de tipos diferentes. O que você pode estar procurando depois é a ordem de avaliação dos argumentos? Não tenho certeza disso, mas talvez você esteja certo: pode acontecer quec
seja avaliado antes(a, b)
mesmo que a vírgula seja associativa à esquerda.struct Foo { Foo() : a(5), b(3) {} int b; int a; }
evaulab(3)
antesa(5)
. Isto é importante se a sua lista é assim:Foo() : a(5), b(a) {}
. b não será definido como 5, mas o valor não inicializado de a, sobre o qual seu compilador pode ou não alertar.O valor de
a
seráb
, mas o valor da expressão serác
. Que está ema seria igual a
b
, ed
seria igual ac
.fonte
a = b; d = c;
?O valor de b será atribuído a a. Nada vai acontecer com c
fonte
O valor de a será igual a b, já que o operador de vírgula tem uma precedência menor que o operador de atribuição.
fonte
Sim O operador de vírgula tem baixa precedência que o operador de atribuição
Saída: i = 3
Porque o operador de vírgula sempre retorna o valor mais à direita.
No caso de operador de vírgula com Operador de atribuição:
Ouput: i = 1
Como sabemos, o operador de vírgula tem precedência menor do que a atribuição .....
fonte
i = 1;
nessa linha?Primeiras coisas primeiro: vírgula não é realmente um operador, para o compilador é apenas um token que obtém um significado no contexto de 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, vamos dar uma olhada neste exemplo:
Normalmente um novato C ++ poderia pensar que esta expressão poderia / iria comparar as coisas, mas é absolutamente errado, o significado da
<
,>
e,
fichas depent no contexto de uso.A interpretação correta do exemplo acima é obviamente que é uma instatação de um modelo.
Exemplo 2:
Quando escrevemos um loop for tipicamente 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, também usamos vírgula:
O significado da vírgula depende do contexto de uso, aqui está o contexto da
for
construção.O que uma vírgula no contexto realmente significa?
Para complicar ainda mais (como sempre em C ++), o operador de vírgula pode ser sobrecarregado (obrigado a Konrad Rudolph por apontar isso).
Para voltar à pergunta, o Código
significa para o compilador algo como
porque a prioridade do
=
token / operador é maior que a prioridade do,
token.e isso é interpretado em contexto como
(observe que a interpretação depende do contexto, aqui não é uma chamada de função / método ou uma instatiação de modelo.)
fonte