operador bool ++ y -

104

Hoy, mientras escribía un código de Visual C ++, me encontré con algo que me sorprendió. Parece que C ++ admite ++ (incremento) para bool, pero no - (decremento). ¿Es solo una decisión aleatoria o hay alguna razón detrás de esto?

Esto compila:

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

Esto no lo hace:

static HMODULE hMod = NULL;
static bool once = true;
if (once--)
    hMod = LoadLibrary("xxx");
Suma
fuente
2
hm, lo mismo para el compilador xcode y gcc
Vladimir
Sí, ++oncey once++trabaja con gcc, pero no con los decrementos.
Justin Ardini
Tal vez vuelva a etiquetar "historial" en lugar de "operador-palabra clave", por lo que esto se agrupa con todas las otras explicaciones divertidas de por qué varias locuras son razonables si se considera el historial. :)
Jon Hanna
Tenga en cuenta que a partir de C ++ 17, el operador de boolpreincremento para está obsoleto, souce .
cogle
esto se puede reemplazar con std::exchange(once,false)(nota: no atómico), si desea algo que no esté obsoleto.
Golvok

Respuestas:

90

Proviene de la historia del uso de valores enteros como booleanos.

Si xes an int, pero lo estoy usando como booleano según, if(x)...entonces incrementar significará que cualquiera que sea su valor de verdad antes de la operación, tendrá un valor de verdad de truedespués de ella (salvo desbordamiento).

Sin embargo, es imposible predecir el resultado de --un conocimiento dado solo del valor de verdad de x, ya que podría resultar en false(si el valor integral es 1) o true(si el valor integral es cualquier otra cosa, en particular, esto incluye 0 [ false] y 2 o más [ true]).

Entonces, como taquigrafía ++funcionó, y --no lo hizo.

++ está permitido en bools por compatibilidad con esto, pero su uso está desaprobado en el estándar.


Esto supone que solo lo uso xcomo booleano, lo que significa que el desbordamiento no puede ocurrir hasta que lo haya hecho con la ++frecuencia suficiente para causar un desbordamiento por sí mismo. Incluso con char como el tipo usado y CHAR_BITSalgo bajo como 5, eso es 32 veces antes de que esto ya no funcione (ese sigue siendo un argumento suficiente para que sea una mala práctica, no estoy defendiendo la práctica, solo explicando por qué funciona) para 32 bits int, por supuesto, tendríamos que usar ++2 ^ 32 veces antes de que esto sea un problema. Con --aunque sólo resultará en falsesi comenzaba con un valor de 1 para true, o se inicia con 0 y se utiliza ++, precisamente, una vez antes.

Esto es diferente si comenzamos con un valor que está solo unos pocos por debajo de 0. De hecho, en tal caso, podríamos querer ++dar como resultado el falsevalor eventualmente, como en:

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

Sin embargo, este ejemplo se trata xcomo en inttodas partes excepto el condicional, por lo que es equivalente a:

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

Lo cual es diferente a usar solo xcomo booleano.

Jon Hanna
fuente
1
Gracias. Es bueno saber que todavía puedo dar respuestas a la gente sobre esto, dado cuánto tiempo hace que escribí una línea de C ++ :)
Jon Hanna
8
Pero si x fuera -1 (VERDADERO en algunas plataformas como VB), ++ x sería FALSO.
James Curran
4
@James, en C y C ++ ese sería el caso en el que estaba pensando cuando dije ("salvo desbordamiento"). En realidad, en VB, cualquier distinto de cero tiene un valor de verdad VERDADERO (como en C), pero tienen -1 en lugar de 1 como resultado de operaciones booleanas verdaderas, ya que NO (VERDADERO) es FALSO, NO (FALSO) es VERDADERO, x O VERDADERO es VERDADERO, x O FALSO es x, xY FALSO es FALSO yxY VERDADERO es x, etc.utilizando los mismos operadores para operaciones booleanas y bit a bit (ya que VB asume dos-complemento entonces -1 es todo 1 bits). Sin embargo, esto puede causar algunos errores extraños en VB si el codificador no detecta que 2 (verdadero) Y 4 (verdadero) dan como resultado 0 (falso).
Jon Hanna
2
@JonHanna: ANSI C89 fue el primer estándar C. El comité ANSI C inventó el <limits.h>encabezado y la CHAR_BITmacro. Antes de eso, supongo que teóricamente podría haber habido implementaciones en las que chares más estrecho que 8 bits, pero hasta donde yo sé, no hubo ninguna. En particular, K & R1 (publicado en 1978) enumera 4 implementaciones de muestra, todas las cuales tienen 8 bits o 9 bits char.
Keith Thompson
1
@JonHanna: Debe tener una implementación C conforme CHAR_BIT >= 8. El estándar no hace concesiones para los objetivos donde eso es difícil. (Podría tener una implementación no conforme, por supuesto.)
Keith Thompson
29

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

5.2.6-2

El operando de sufijo - se decrementa de forma análoga al operador de sufijo ++, excepto que el operando no debe ser de tipo bool. [Nota: Para el incremento y decremento de prefijo, consulte 5.3.2. ]

Y como era de esperar ...

5.3.2-2

El operando de prefijo - se modifica restando 1. El operando no debe ser de tipo bool. Los requisitos del operando de prefijo y las propiedades de su resultado son, por lo demás, las mismas que las de prefijo ++. [Nota: Para incrementos y decrementos de sufijo, consulte 5.2.6. ]

Además, 5.6.2-1 y 5.3.2-1 mencionan que ++ para bools debe ser verdadero y el Anexo D-1 dice que ++ en bools está desaprobado.

Mainframe nórdico
fuente
3
@BlueRaja: Vea la respuesta de Jon Hanna.
Justin Ardini
9

Esto fue apoyado por razones históricas. Pero tenga en cuenta que ... El uso de un operando de tipo bool con el operador ++ está en desuso, consulte la Sección 5.3.2 en el estándar C ++ (n3092)

5.3.2 Incremento y decremento [expr.pre.incr]

  • El operando del prefijo ++ se modifica agregando 1, o se establece en verdadero si es bool (este uso está en desuso). El operando será un valor modificable. El tipo de operando será un tipo aritmético o un puntero a un tipo de objeto completamente definido. El resultado es el operando actualizado; es un valor l, y es un campo de bits si el operando es un campo de bits. Si x no es de tipo bool, la expresión ++ x es equivalente ax + = 1 [Nota: consulte las discusiones sobre los operadores de suma (5.7) y asignación (5.17) para obtener información sobre conversiones. —Nota final]
  • El operando de prefijo - se modifica restando 1. El operando no debe ser de tipo bool. Los requisitos del operando de prefijo y las propiedades de su resultado son, por lo demás, las mismas que las de prefijo ++.
Abhay
fuente
3
  • Con los estándares antiguos (C ++ 98) no es un error.
  • Con los nuevos estándares, incrementar un booleano está en desuso. (C ++ 11)
  • Puede usar el incremento en un booleano hasta C ++ 17.
Mustafagonul
fuente