¿Está bien definida la operación "falso <verdadero"?

153

La definición de C ++ define:

  1. la existencia del operador 'menor que' para parámetros booleanos, y si es así,
  2. ¿El resultado de las 4 permutaciones de parámetros?

En otras palabras, ¿los resultados de las siguientes operaciones están definidos por la especificación?

false < false
false < true
true < false
true < true

En mi configuración (Centos 7, gcc 4.8.2), el siguiente código escupe lo que esperaría (dado el historial de C de representar falso como 0 y verdadero como 1):

false < false = false
false < true = true
true < false = false
true < true = false

Si bien estoy bastante seguro de que la mayoría de los compiladores (¿todos?) Darán el mismo resultado, ¿está esto legislado por la especificación C ++? ¿O es un ofuscador, pero el compilador que cumple con las especificaciones puede decidir que verdadero es menos que falso?

#include <iostream>

const char * s(bool a)
{
  return (a ? "true" : "false");
}

void test(bool a, bool b)
{
  std::cout << s(a) << " < " << s(b) << " = " << s(a < b) << std::endl;
}

int main(int argc, char* argv[])
{
  test(false, false);
  test(false, true);
  test(true, false);
  test(true, true);
  return 0;
}
Duncan
fuente
66
@ Exterior Hay usos válidos. Tal como usar std::minen std::vector<bool>as &&.
Angew ya no está orgulloso de SO
19
@Ulterior si puede resolver una buena pregunta que aún no se ha formulado después de todos estos años de StackOverflow, se merece algunos puntos. No es trolling.
Mark Ransom
35
@Ulterior La motivación para preguntar es genuina: soy bastante nuevo en C ++ (proveniente de C) y quiero almacenar algunos objetos en un std :: set <>. Mi implementación del operador <de mi objeto se basa principalmente en una propiedad booleana del objeto, seguida de otras propiedades de identificación secundarias. Al iterar sobre el conjunto, quiero estar seguro de que los objetos 'falsos' son lo primero. Si bien me funciona aquí y ahora, estoy seguro de que está garantizado para funcionar en todas las plataformas (incluidas las integradas) sin tener que recurrir innecesariamente al uso de (a? 1: 0), o similar, en el objeto < operador.
Duncan
26
Una consecuencia inquietante es que p <= qsignifica p implies qcuándo py qson de tipo bool!
Theodore Norvell
44
@Technophile Presumiblemente, lo que es perturbador es que <=podría leerse inadvertidamente como una flecha izquierda, y que la flecha "solo si" (es decir, [implica [materialmente] ") a veces se compone de un tipo o se escribe de manera informal =>(es decir, con un eje doble parecido =) . Una flecha izquierda incluso se lee a veces como "si", aunque creo que esto es mucho menos común que el uso de una flecha derecha para "solo si".
Eliah Kagan

Respuestas:

207

TL; DR:

Las operaciones están bien definidas de acuerdo con el borrador del estándar C ++.

Detalles

Podemos ver eso yendo al borrador de la sección estándar de C ++ 5.9 Operadores relacionales que dice ( énfasis mío en adelante ):

Los operandos deben tener aritmética , enumeración o tipo de puntero , o tipo std :: nullptr_t. Los operadores <(menor que),> (mayor que), <= (menor o igual que) y> = (mayor o igual que) todos producen falso o verdadero. El tipo del resultado es bool

y bools son tipos aritmáticos de 3.9.1 Tipos fundamentales

Los tipos bool , char, char16_t, char32_t, wchar_t y los tipos enteros con y sin signo se denominan colectivamente tipos integrales.

y

Los tipos integrales y flotantes se denominan colectivamente tipos aritméticos.

y truey falseson literales booleanos de literales 2.14.6booleanos:

boolean-literal:
    false
    true

Volviendo a la sección 5.9para ver más a fondo la mecánica de los operadores relacionales, dice:

Las conversiones aritméticas habituales se realizan en operandos de tipo aritmético o de enumeración.

Las conversiones aritméticas habituales se tratan en la sección 5que dice:

De lo contrario, las promociones integrales (4.5) se realizarán en ambos operandos

y la sección 4.5dice:

Un valor de tipo bool se puede convertir en un valor de tipo int, con falso convirtiéndose en cero y verdadero convirtiéndose en uno.

y entonces las expresiones:

false < false
false < true
true < false
true < true

El uso de estas reglas se convierte en:

0 < 0
0 < 1
1 < 0
1 < 1
Shafik Yaghmour
fuente
66
Bien, eso es tan explícito como podría ser cualquier respuesta, aunque aún es fácil de leer. A nit: Creo que has marcado en negrita el "tipo" incorrecto: "Los operandos tendrán aritmética , enumeración o tipo de puntero , o tipo std :: nullptr_t". Agregar paréntesis para mayor claridad da ((aritmética, enumeración o puntero) tipo) o (tipo std :: nullptr_t).
No es que cambie su respuesta, pero N3485 [over.built] / 12: Para cada par de tipos aritméticos promovidos L y R, existen funciones de operador candidato de la forma ... operador bool <(L, R); - ¿No se promueven los argumentos antes de que se apliquen las reglas que usted cita?
Chris
@chris No estoy muy familiarizado con esa sección, así que tendría que pensarlo, pero no creo que la respuesta cambie de lo que puedo ver.
Shafik Yaghmour
Sí, la promoción es lo primero que sucede de cualquier manera.
Chris
63

Los valores booleanos están sujetos a las promociones enteras habituales, con falsedefinido como 0y truedefinido como 1. Eso hace que todas las comparaciones estén bien definidas.

Mark Ransom
fuente
2
... y los operadores relacionales se especifican para realizar las conversiones aritméticas habituales (que incluyen promociones de enteros) en operandos de tipo aritmético o de enumeración.
TC
55
Me gusta que esta respuesta sea más corta que la de Shafik, pero creo que el punto clave que falsese define como 0y truese define como 1 en el estándar (en lugar de solo por la práctica común) necesita evidencia para respaldarlo.
KRyan
@KRyan qué, ¿no vas a tomar mi palabra? :) Antes de que existiera un booltipo, incluso antes de que existiera C ++, el resultado de una operación booleana se definió como 0falso y 1verdadero. No me sorprendería si lo puedes encontrar en K + R.
Mark Ransom
1
@KRyan No puedo retroceder tanto como K + R, pero saqué mi copia del estándar ANSI C de 1990. La Sección 6.3.8 dice "Cada uno de los operadores <(menor que), >(mayor que), <=(menor o igual que), y >=(mayor o igual que) producirá 1 si la relación especificada es verdadera y 0 si es falso. El resultado tiene tipo int".
Mark Ransom
1
El mayor problema del IIRC fue que en K&R, enum bool { false = 0, true = 1}era legal pero no definía un operator<.
MSalters
22

De acuerdo con el estándar C ++ (5.9 operadores relacionales)

2 Las conversiones aritméticas habituales se realizan en operandos de tipo aritmético o de enumeración.

y

1 ... El tipo de resultado es bool.

y (3.9.1 Tipos fundamentales)

6 Los valores de tipo bool son verdaderos o falsos.49 [Nota: No hay tipos o valores de bool con signo, sin signo, cortos o largos. —Nota final] Los valores de tipo bool participan en promociones integrales (4.5).

y (4.5 Promociones integrales)

6 Un valor de tipo bool se puede convertir en un valor de tipo int, con falso convirtiéndose en cero y verdadero convirtiéndose en uno .

Entonces, en todos sus ejemplos, verdadero se convierte en int 1 y falso se convierte en int 0

Estas expresiones

false < false
false < true
true < false
true < true

son enteramente equivalentes a

0 < 0
0 < 1
1 < 0
1 < 1
Vlad de Moscú
fuente
8

Boolean falsees equivalente a int 0, y boolean truees equivalente a int 1. Entonces esto explica por qué la expresión false < true=> 0 < 1es la única que regresa true.

Ladrón de ciegos
fuente