J'ai eu un étrange bug dans mon programme, et après quelques heures de débogage, j'ai trouvé la ligne très stupide suivante:
int a = b * (c * d * + e)
Si vous ne le voyez pas: entre d
et e
j'ai écrit * +
, où juste un +
était voulu.
Pourquoi est-ce compilé et qu'est-ce que cela signifie réellement?
Le +
est interprété comme un opérateur unaire plus. Il renvoie simplement la valeur promue de son opérande.
Unaire +
renvoie la valeur promue.
Unaire -
renvoie la négation:
int a = 5;
int b = 6;
unsigned int c = 3;
std::cout << (a * +b); // = 30
std::cout << (a * -b); // = -30
std::cout << (1 * -c); // = 4294967293 (2^32 - 3)
Cela compile parce que le +
est interprété comme unaire plus, qui effectuera les promotions intégrales sur les types intégraux ou d'énumération et le résultat aura le type de l'opérande promu.
En supposant que e
est un type d'énumération intégrale ou non étendue, les promotions intégrales finiraient par s'appliquer de toute façon puisque *
applique les conversions arithmétiques habituelles à ses opérandes qui aboutissent aux promotions intégrales pour les types intégraux.
Du projet de norme C++ 5.3.1
[expr.unary.op] :
L'opérande de l'opérateur unaire + doit être de type arithmétique, énumération non étendue ou pointeur et le résultat est la valeur de l'argument. La promotion intégrale est effectuée sur des opérandes intégraux ou d'énumération. Le type du résultat est le type de l'opérande promu.
Les promotions intégrales sont traitées dans la section 4.5
[conv.prom] et si les variables e
sont d'un type autre que bool, char16_t, char32_t, or wchar_t
et dont le taux de conversion est inférieur à int , il serait alors couvert par le paragraphe 1
:
Une valeur d'un type entier autre que bool, char16_t, char32_t ou wchar_t dont le rang de conversion entier (4.13) est inférieur au rang de int peut être convertie en une valeur de type int si int peut représenter toutes les valeurs du type source ; sinon, la valeur source peut être convertie en une valeur de type unsigned int.
Pour un ensemble complet de cas, nous pouvons regarder cppreference .
Unary plus peut également être utile dans certains cas pour résoudre l'ambiguïté, un cas intéressant serait de Résolution de la surcharge ambiguë sur le pointeur de fonction et std :: fonction pour un lambda utilisant + .
Remarque, pour ces réponses, se référant à unaire -
et les valeurs négatives, cela est trompeur, comme le montre cet exemple:
#include <iostream>
int main()
{
unsigned x1 = 1 ;
std::cout << -x1 << std::endl ;
}
ce qui se traduit par:
4294967295
Voir en direct en utilisant gcc sur wandbox .
Il est intéressant de noter que unaire plus a été ajouté à C99 pour la symétrie avec unaire moins, à partir de Justification de la norme internationale — Langages de programmation — C :
Unary plus a été adopté par le Comité C89 à partir de plusieurs implémentations, pour la symétrie avec unary moins.
et je ne peux pas trouver un bon cas où le casting ne serait pas suffisant pour obtenir la même promotion/conversion souhaitée. L'exemple lambda que je cite ci-dessus, en utilisant unaire plus pour forcer une expression lambda à être convertie en un pointeur de fonction:
foo( +[](){} ); // not ambiguous (calls the function pointer overload)
pourrait être accompli en utilisant un cast explicite:
foo( static_cast<void (*)()>( [](){} ) );
et on pourrait faire valoir que ce code est meilleur car l'intention est explicite.
Il convient de noter que Manuel de référence C++ annoté ( [~ # ~] bras [~ # ~] ) il contient le commentaire suivant :
Unary plus est un accident historique et généralement inutile.
Comme ils l'ont expliqué, (+) et (-) viennent d'être utilisés comme opérateur unaire:
Opérateurs unaires agir sur un seul opérande dans une expression
int value = 6;
int negativeInt = -5;
int positiveInt = +5;
cout << (value * negativeInt); // 6 * -5 = -30
cout << (value * positiveInt); // 6 * +5 = 30
cout << (value * - negativeInt); // 6 * -(-5) = 30
cout << (value * + negativeInt); // 6 * +(-5) = -30
cout << (value * - positiveInt); // 6 * -(+5) = -30
cout << (value * + positiveInt); // 6 * +(+5) = 30
donc à partir de votre code:
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int a = b * (c * d * + e)
//result: 2 * (3 * 4 * (+5) ) = 120
Pour ajouter une touche supplémentaire aux bonnes réponses déjà données ici, si vous compilez avec l'indicateur -s, le compilateur C affichera un fichier d'assemblage dans lequel les instructions générées peuvent être examinées. Avec le code C suivant:
int b=1, c=2, d=3, e=4;
int a = b * (c * d * + e);
L'assembly généré (en utilisant gcc, en compilant pour AMD64) commence par:
movl $1, -20(%ebp)
movl $2, -16(%ebp)
movl $3, -12(%ebp)
movl $4, -8(%ebp)
afin que nous puissions identifier les positions de mémoire individuelles -20 (% ebp) comme variable b, jusqu'à -8 (% ebp) comme variable e. -4 (% epp) est la variable a. Maintenant, le calcul est rendu comme suit:
movl -16(%ebp), %eax
imull -12(%ebp), %eax
imull -8(%ebp), %eax
imull -20(%ebp), %eax
movl %eax, -4(%ebp)
Ainsi, comme cela a été commenté par d'autres personnes répondant, le compilateur traite simplement "+ e" comme l'opération positive unaire. La première instruction movl place le contenu de la variable e dans le registre accumulateur EAX, qui est ensuite rapidement multiplié par le contenu de la variable d ou -12 (% ebp), etc.
Pourquoi compile-t-il? Il compile parce que +
est analysé comme opérateur unaire plus, et non comme opérateur d'addition. Le compilateur essaie d'analyser autant que possible sans générer d'erreurs de syntaxe. Donc ça:
d * + e
Est analysé comme:
d
(opérande)*
(opérateur de multiplication)+
(opérateur unaire plus) e
(opérande)Attendu que ceci:
d*++e;
Est analysé comme:
d
(opérande)*
(opérateur de multiplication)++
(opérateur de pré-incrémentation) e
(opérande)De plus, ceci:
d*+++e;
Est analysé comme:
d
(opérande)*
(opérateur de multiplication)++
(opérateur de pré-incrémentation) +
(opérateur unaire plus) e
(opérande)Notez qu'il ne crée pas une erreur de syntaxe mais l'erreur du compilateur "LValue requrired".
Ce ne sont que des mathématiques de base. Par exemple:
5 * -4 = -20
5 * +4 = 5 * 4 = 20
-5 * -4 = 20
Négatif * Négatif = Positif
Positif * Négatif = Négatif
Positif * Positif = Positif
C'est l'explication la plus simple qui soit.
Le moins (-) et le plus (+) indiquent simplement si le nombre est positif ou négatif.