operador bool ++ e -

104

Hoje, ao escrever algum código Visual C ++, encontrei algo que me surpreendeu. Parece que C ++ suporta ++ (incremento) para bool, mas não - (decremento). É apenas uma decisão aleatória ou há algum motivo por trás disso?

Isso compila:

static HMODULE hMod = NULL;
static bool once = false;
if (!once++)
    hMod = LoadLibrary("xxx");

Isso não:

static HMODULE hMod = NULL;
static bool once = true;
if (once--)
    hMod = LoadLibrary("xxx");
Suma
fonte
2
hm, o mesmo para xcode e compilador gcc
Vladimir
Sim, ++oncee once++trabalhar com gcc, mas não com decrementos.
Justin Ardini
Talvez retag "história" em vez de "operador-palavra-chave", então isso é agrupado com todas as outras explicações divertidas de por que várias coisas malucas são razoáveis ​​se você considerar a história? :)
Jon Hanna
Nota como de C ++ 17 o operador pré-incremento para boolé preterido, souce .
cogle
ele pode ser substituído por std::exchange(once,false)(nota: não atômico), se você quiser algo não obsoleto.
golvok de

Respostas:

90

Ele vem da história do uso de valores inteiros como booleanos.

Se xfor um int, mas estou usando-o como um booleano, if(x)...então incrementar significará que qualquer que seja o seu valor verdade antes da operação, terá um valor verdade truedepois (barrando o estouro).

No entanto, é impossível prever o resultado de --determinado conhecimento apenas do valor de verdade de x, já que poderia resultar em false(se o valor integral for 1) ou true(se o valor integral for qualquer outra coisa - notavelmente isso inclui 0 [ false] e 2 ou mais [ true]).

Então, como um atalho ++funcionou, e --não funcionou .

++ é permitido em bools para compatibilidade com isso, mas seu uso está obsoleto no padrão.


Isso pressupõe que eu use apenasx como booleano, o que significa que o estouro não pode acontecer até que eu tenha feito o ++suficiente para causar um estouro por conta própria. Mesmo com char como o tipo usado e CHAR_BITSalgo baixo como 5, isso é 32 vezes antes que isso não funcione mais (isso ainda é um argumento suficiente para ser uma prática ruim, não estou defendendo a prática, apenas explicando por que funciona) para 32 bits, inté claro que teríamos de usar ++2 ^ 32 vezes antes que isso fosse um problema. Com --embora, ele só resultará em falsese eu começar com um valor de 1 para true, ou começar com 0 e usar ++precisamente uma vez antes.

Isso é diferente se começarmos com um valor que é apenas alguns abaixo de 0. Na verdade, nesse caso, podemos querer ++resultar no falsevalor eventualmente, como em:

int x = -5;
while(++x)
  doSomething(x);

No entanto, este exemplo trata xcomo um inttodo, exceto o condicional, portanto, é equivalente a:

int x = -5;
while(++x != 0)
  doSomething(x);

O que é diferente de usar apenas xcomo booleano.

Jon Hanna
fonte
1
Obrigado. É bom saber que ainda posso dar respostas às pessoas assim, dado o tempo que se passou desde que eu realmente escrevi uma linha em C ++ :)
Jon Hanna
8
Mas se x fosse -1 (VERDADEIRO em algumas plataformas como VB), ++ x seria FALSO.
James Curran
4
@James, em C e C ++ esse seria o caso em que estava pensando quando disse ("barrando estouro"). Na verdade, em VB, qualquer valor diferente de zero tem valor verdadeiro TRUE (como em C), mas eles têm -1 em vez de 1 como resultado de operações booleanas verdadeiras, pois NOT (TRUE) é FALSE, NOT (FALSE) é TRUE, x OR TRUE é TRUE, x OR FALSE é x, x AND FALSE é FALSE e x AND TRUE é x, etc. usando os mesmos operadores para operações booleanas e bit a bit (uma vez que VB assume complemento de dois, então -1 é todos 1 bits). No entanto, isso pode causar alguns bugs estranhos no VB se o codificador não detectar que 2 (verdadeiro) E 4 (verdadeiro) resulta em 0 (falso).
Jon Hanna de
2
@JonHanna: ANSI C89 foi o primeiro padrão C. O comitê ANSI C inventou o <limits.h>cabeçalho e a CHAR_BITmacro. Antes disso, suponho que teoricamente poderia ter havido implementações onde charé mais estreito do que 8 bits, mas até onde eu sei não havia nenhuma. Em particular, K & R1 (publicado em 1978) lista 4 exemplos de implementação, todos com 8 ou 9 bits char.
Keith Thompson
1
@JonHanna: Uma implementação C conforme deve ter CHAR_BIT >= 8. O padrão não permite alvos onde isso é difícil. (Você poderia ter uma implementação não conforme, é claro.)
Keith Thompson
29

ANSI ISO IEC 14882 2003 (c ++ 03):

5.2.6-2

O operando de postfix - é decrementado analogamente ao operador postfix ++, exceto que o operando não deve ser do tipo bool. [Nota: Para incremento e decremento do prefixo, consulte 5.3.2. ]

E sem surpresa ...

5.3.2-2

O operando do prefixo - é modificado subtraindo 1. O operando não deve ser do tipo bool. Os requisitos do operando de prefixo - e as propriedades de seu resultado são as mesmas do prefixo ++. [Nota: Para incremento e decremento pós-fixados, consulte 5.2.6. ]

Além disso, 5.6.2-1 e 5.3.2-1 mencionam que ++ para bools deve ser verdadeiro e o Anexo D-1 diz que ++ para bools está obsoleto.

Nordic Mainframe
fonte
3
@BlueRaja: Veja a resposta de Jon Hanna.
Justin Ardini
9

Por razões históricas, isso foi apoiado. Mas observe que ... O uso de um operando do tipo bool com o operador ++ está obsoleto, consulte a Seção 5.3.2 no Padrão C ++ (n3092)

5.3.2 Incremento e decremento [expr.pre.incr]

  • O operando do prefixo ++ é modificado pela adição de 1 ou definido como verdadeiro se for bool (esse uso está obsoleto). O operando deve ser um valor modificável. O tipo de operando deve obrigatoriamente ser um tipo aritmético ou um ponteiro para um tipo de objeto completamente definido. O resultado é o operando atualizado; é um lvalue e é um campo de bits se o operando for um campo de bits. Se x não for do tipo bool, a expressão ++ x é equivalente ax + = 1 [Observação: consulte as discussões sobre adição (5.7) e operadores de atribuição (5.17) para obter informações sobre conversões. —Enviar nota]
  • O operando do prefixo - é modificado subtraindo 1. O operando não deve ser do tipo bool. Os requisitos do operando de prefixo - e as propriedades de seu resultado são as mesmas do prefixo ++.
Abhay
fonte
3
  • Com os padrões antigos (C ++ 98), não é um erro.
  • Com os novos padrões, incrementar um booleano está obsoleto. (C ++ 11)
  • Você pode usar a incrementação em um booleano até C ++ 17.
mustafagonul
fonte