L'opération «faux <vrai» est-elle bien définie?

153

La spécification C ++ définit-elle:

  1. l'existence de l'opérateur 'inférieur à' pour les paramètres booléens, et si c'est le cas,
  2. le résultat des 4 permutations de paramètres?

En d'autres termes, les résultats des opérations suivantes sont-ils définis par la spécification?

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

Sur ma configuration (Centos 7, gcc 4.8.2), le code ci-dessous crache ce à quoi je m'attendais (étant donné l'historique de C de représenter faux comme 0 et vrai comme 1):

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

Bien que je sois à peu près sûr que la plupart (tous?) Des compilateurs donneront le même résultat, est-ce que cela est légiféré par la spécification C ++? Ou un compilateur obscur, mais conforme aux spécifications, est-il autorisé à décider que true est moins que false?

#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
la source
6
@Ulterior Il y a des utilisations valides. Tels que l'utilisation std::minsur std::vector<bool>as &&.
Angew n'est plus fier de SO
19
@Ulterior si vous pouvez trouver une bonne question qui n'a pas encore été posée après toutes ces années de StackOverflow, vous méritez quelques points. Ce n'est pas de la pêche à la traîne.
Mark Ransom
35
@Ulterior La motivation pour demander est authentique: je suis assez nouveau en C ++ (venant de C) et je veux stocker des objets dans un std :: set <>. Ma mise en œuvre de l'opérateur <de mon objet est principalement basée sur une propriété booléenne de l'objet, suivie d'autres propriétés d'identification secondaires. Lors de l'itération sur l'ensemble, je veux être sûr que les «faux» objets viennent en premier. Bien que cela fonctionne pour moi ici et maintenant, je cherche à me rassurer qu'il fonctionne sur toutes les plates-formes (y compris les plates-formes intégrées) sans avoir à recourir inutilement à l'utilisation de (a? 1: 0), ou similaire, dans mon objet < opérateur.
duncan
26
Une conséquence inquiétante est que cela p <= qsignifie p implies qquand pet qsont de type booléen!
Theodore Norvell
4
@Technophile Ce qui est vraisemblablement dérangeant, c'est qu'elle <=pourrait être lue par inadvertance comme une flèche vers la gauche , et que la flèche de droite "seulement si" (c'est-à-dire "[matériellement] implique") est parfois composée ou écrite de manière informelle de la même manière =>(c'est- à -dire avec un arbre doublé ressemblant à =) . Une flèche vers la gauche est même parfois lue comme «si», même si je pense que c'est beaucoup moins courant que l'utilisation d'une flèche vers la droite pour «seulement si».
Eliah Kagan

Réponses:

207

TL; DR:

Les opérations sont bien définies selon le projet de norme C ++.

Détails

On peut voir qu'en allant au projet standard C ++ section 5.9 Opérateurs relationnels qui dit ( Souligné par l'auteur à l' avenir ):

Les opérandes ont l' arithmétique , énumération ou pointeur de type ou d'un type std :: nullptr_t. Les opérateurs <(inférieur à),> (supérieur à), <= (inférieur ou égal à) et> = (supérieur ou égal à) donnent tous faux ou vrai. Le type du résultat est booléen

et les booléens sont des types arithématiques de 3.9.1 Types fondamentaux

Les types bool , char, char16_t, char32_t, wchar_t et les types entiers signés et non signés sont collectivement appelés types intégraux.

et

Les types intégraux et flottants sont appelés collectivement types arithmétiques.

et trueet falsesont des littéraux booléens à partir de 2.14.6littéraux booléens:

boolean-literal:
    false
    true

Revenant à la section 5.9pour voir plus loin la mécanique des opérateurs relationnels, il est dit:

Les conversions arithmétiques usuelles sont effectuées sur des opérandes de type arithmétique ou énumération.

les conversions arithmétiques habituelles sont couvertes dans la section 5qui dit:

Sinon, les promotions intégrales (4.5) doivent être effectuées sur les deux opérandes

et la section 4.5dit:

Une prvalue de type bool peut être convertie en une prvalue de type int, avec false devenant zéro et true devenant un.

et donc les expressions:

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

en utilisant ces règles deviennent:

0 < 0
0 < 1
1 < 0
1 < 1
Shafik Yaghmour
la source
6
Bien, c'est à peu près aussi explicite que n'importe quelle réponse, tout en restant facile à lire. Un nit: Je pense que vous le mauvais gras « type »: « Les opérandes ont l' arithmétique , énumération ou pointeur de type ou d'un type std :: nullptr_t. » L'ajout de parenthèses pour plus de clarté donne ((arithmétique, énumération ou pointeur) type) ou (type std :: nullptr_t).
Non pas que cela change votre réponse, mais N3485 [over.built] / 12: Pour chaque paire de types arithmétiques promus L et R, il existe des fonctions d'opérateurs candidats de la forme ... opérateur booléen <(L, R); - Les arguments avancés avant que les règles que vous citez ne s'appliquent même?
chris
@chris Je ne suis pas très familier avec cette section donc je devrais y réfléchir mais je ne pense pas que la réponse change de ce que je peux voir.
Shafik Yaghmour
Ouais, la promotion est la première chose à arriver de toute façon.
chris
63

Les valeurs booléennes sont soumises aux promotions entières habituelles, falsedéfinies comme 0et truedéfinies comme 1. Cela rend toutes les comparaisons bien définies.

Mark Ransom
la source
2
... et les opérateurs relationnels sont spécifiés pour effectuer les conversions arithmétiques habituelles (qui incluent les promotions d'entiers) sur les opérandes de type arithmétique ou énumération.
TC
5
J'aime que cette réponse soit plus courte que celle de Shafik, mais je pense que le point clé qui falseest défini 0et qui trueest défini comme 1 dans la norme (plutôt que simplement par la pratique courante) a besoin de preuves pour le soutenir.
KRyan
@KRyan quoi, tu ne vas pas me croire sur parole? :) Avant qu'il y ait un booltype, avant même qu'il y ait C ++, le résultat d'une opération booléenne était défini comme 0pour false et 1pour true. Je ne serais pas surpris si vous pouvez le trouver dans K + R.
Mark Ransom
1
@KRyan Je ne peux pas revenir aussi loin que K + R, mais j'ai extrait ma copie du standard ANSI C 1990. La section 6.3.8 dit: "Chacun des opérateurs <(inférieur à), >(supérieur à), <=(inférieur ou égal à) et >=(supérieur ou égal à) donnera 1 si la relation spécifiée est vraie et 0 si elle est false. Le résultat est de type int. "
Mark Ransom
1
Le plus gros problème de l'IIRC était que dans K&R, enum bool { false = 0, true = 1}était légal mais ne définissait pas un fichier operator<.
MSalters
22

Selon la norme C ++ (5.9 Opérateurs relationnels)

2 Les conversions arithmétiques usuelles sont effectuées sur des opérandes de type arithmétique ou énumération.

et

1 ... Le type du résultat est booléen.

et (3.9.1 Types fondamentaux)

6 Les valeurs de type bool sont vraies ou fausses.49 [Remarque: il n'y a pas de types ou de valeurs booléens signés, non signés, courts ou longs. —End note] Les valeurs de type bool participent aux promotions intégrales (4.5).

et (4.5 promotions intégrales)

6 Une prvalue de type bool peut être convertie en une prvalue de type int, avec false devenant zéro et true devenant un .

Donc, dans tous vos exemples, true est converti en int 1 et false est converti en int 0

Ces expressions

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

sont entièrement équivalents à

0 < 0
0 < 1
1 < 0
1 < 1
Vlad de Moscou
la source
8

Boolean falseest équivalent à int 0, et boolean trueest équivalent à int 1. Cela explique donc pourquoi l'expression false < true=> 0 < 1est la seule qui renvoie true.

Voleur aveugle
la source