web-dev-qa-db-fra.com

Pourquoi utiliser !! lors de la conversion int en bool?

Quelle peut être une raison pour convertir un entier en booléen de cette manière?

bool booleanValue = !!integerValue;

au lieu de juste

bool booleanValue = integerValue;

Tout ce que je sais, c'est que dans VC++ 7, ce dernier provoquera avertissement C48 et pas le premier. Y a-t-il une autre différence entre les deux?

76
sharptooth

Les problèmes avec le "!!" l'idiome est qu'il est laconique, difficile à voir, facile à confondre avec une faute de frappe, facile à déposer l'un des "!", et ainsi de suite. Je l'ai mis dans la catégorie "regardez comme nous pouvons être mignons avec C/C++".

Écrivez simplement bool isNonZero = (integerValue != 0); ... soyez clair.

111
user143506

Historiquement, le !! idiom a été utilisé pour s'assurer que votre bool contenait vraiment l'une des deux valeurs attendues dans une variable semblable à bool, car C et C++ n'avaient pas un vrai type bool et nous avons truqué avec ints. C'est moins un problème maintenant avec les "vrais" bools.

Mais en utilisant !! est un moyen efficace de documenter (à la fois pour le compilateur et pour les futurs utilisateurs de votre code) que oui, vous aviez vraiment l'intention de convertir ce int en bool.

49
T.J. Crowder

Il est utilisé parce que le langage C (et certains compilateurs C++ pré-standard également) n'avaient pas le type bool, juste int. Les int ont donc été utilisés pour représenter des valeurs logiques: 0 était censé signifier false, et tout le reste était true. Le ! l'opérateur renvoyait 1 de 0 et 0 de tout le reste. Double ! a été utilisé pour inverser ceux-ci, et il était là pour s'assurer que la valeur est juste 0 ou 1 selon sa valeur logique.

En C++, depuis l'introduction d'un type bool approprié, il n'est plus nécessaire de le faire. Mais vous ne pouvez pas simplement mettre à jour toutes les sources héritées, et vous ne devriez pas avoir à le faire, en raison de la compatibilité descendante de C avec C++ (la plupart du temps). Mais beaucoup de gens le font toujours, pour la même raison: pour conserver leur code rétrocompatible avec les anciens compilateurs qui ne comprennent toujours pas bools.

Et c'est la seule vraie réponse. D'autres réponses sont trompeuses.

14
SasQ

Parce que! IntegerValue signifie integerValue == 0 et !! integerValue signifie donc integerValue! = 0, une expression valide renvoyant un booléen. Ce dernier est un casting avec perte d'informations.

13
StampedeXV

Une autre option est l'opérateur ternaire qui semble générer une ligne de moins de code Assembly (dans Visual Studio 2005 de toute façon):

bool ternary_test = ( int_val == 0 ) ? false : true;

qui produit le code d'assemblage:

cmp DWORD PTR _int_val$[ebp], 0
setne   al
mov BYTE PTR _ternary_test$[ebp], al

Contre:

bool not_equal_test = ( int_val != 0 );

qui produit:

xor eax, eax
cmp DWORD PTR _int_val$[ebp], 0
setne   al
mov BYTE PTR _not_equal_test$[ebp], al

Je sais que ce n'est pas une énorme différence, mais j'étais curieux à ce sujet et pensais simplement que je partagerais mes conclusions.

6
Brian

Un booléen ne peut avoir que deux états, 0 et 1. Un entier peut avoir n'importe quel état compris entre -2147483648 et 2147483647 en supposant un entier signé 32 bits. L'unaire! L'opérateur sort 1 si l'entrée est 0 et sort 0 si l'entrée est autre que 0. Donc! 0 = 1 et! 234 = 0. La seconde! commute simplement la sortie pour que 0 devienne 1 et 1 devienne 0.

Ainsi, la première instruction garantit que booleanValue sera définie égale à 0 ou 1 et aucune autre valeur, la deuxième instruction ne le fait pas.

5
John Scipione

!! est un moyen idiomatique de convertir en bool, et il fonctionne pour fermer l'avertissement idiot du compilateur Visual C++ sur l'inefficacité présumée d'une telle conversion.

Je vois par les autres réponses et commentaires que beaucoup de gens ne connaissent pas l'utilité de cet idiome dans la programmation Windows. Ce qui signifie qu'ils n'ont pas fait de programmation Windows sérieuse. Et supposez aveuglément que ce qu'ils ont rencontré est représentatif (ce n'est pas le cas).

#include <iostream>
using namespace std;

int main( int argc, char* argv[] )
{
    bool const b = static_cast< bool >( argc );
    (void) argv;
    (void) b;
}
> [d:\dev\test] 
> cl foo.cpp
 foo.cpp 
 foo.cpp (6): avertissement C4800: 'int': forcer la valeur à bool 'true' ou 'false' (avertissement de performance) 
 
 [d:\dev\test] 
> _ 

Et au moins une personne pense que si un novice absolu ne reconnaît pas sa signification, alors c'est mauvais. C'est stupide. Il y a beaucoup de novices qui ne reconnaissent ni ne comprennent. Écrire son code pour qu'il soit compris par tout novice n'est pas quelque chose pour les professionnels. Pas même pour les étudiants. Commencer sur la voie de l'exclusion des opérateurs et des combinaisons d'opérateurs que les novices absolus ne reconnaissent pas ... Eh bien, je n'ai pas les mots pour donner à cette approche une description appropriée, désolé.

Santé et hth.,

4

La réponse de user143506 est correcte mais pour un éventuel problème de performances j'ai comparé les possibilités en asm:

return x;, return x != 0;, return !!x; Et même return boolean_cast<bool>(x) donne cet ensemble parfait d'instructions asm:

test    edi/ecx, edi/ecx
setne   al
ret

Cela a été testé pour GCC 7.1 et MSVC 19 2017. (Seul le boolean_converter dans MSVC 19 2017 entraîne une plus grande quantité de code asm mais cela est dû à la templatisation et aux structures et peut être négligé par un point de vue de performance, car le même les lignes comme indiqué ci-dessus peuvent simplement être dupliquées pour différentes fonctions avec le même runtime.)

Cela signifie: il n'y a pas de différence de performances.

PS: Ce boolean_cast a été utilisé:

#define BOOL int
// primary template
template< class TargetT, class SourceT >
struct boolean_converter;

// full specialization
template< >
struct boolean_converter<bool, BOOL>
{
  static bool convert(BOOL b)
  {
    return b ? true : false;
  }
};

// Type your code here, or load an example.
template< class TargetT, class SourceT >
TargetT boolean_cast(SourceT b)
{
  typedef boolean_converter<TargetT, SourceT> converter_t;
  return converter_t::convert(b);
}

bool is_non_zero(int x) {
   return boolean_cast< bool >(x);
}
1
kallitokaco