web-dev-qa-db-fra.com

Pourquoi «auto» ne respecte-t-il pas l'opérateur unaire moins?

Je suis assez nouveau en C++ mais je trouve ce comportement de auto bizarre:

class A{};

int main() {
    A a;
    auto x = -(sizeof(a));
    cout << x << endl;
    return 0;
}

La variable x est unsigned dans ce cas, bien que j'aie utilisé l'opérateur unaire moins lors de l'initialisation de la variable. Comment se fait-il que seul le type de retour de sizeof (std::size_t) est pris en compte mais pas le fait que le nombre stocké sera négatif à cause de l'opérateur utilisé?

Je suis conscient de size_t étant un entier non signé.

J'ai essayé cela avec GCC 8.1.0 et C++ 17.

32
CodeShark

Le problème réel ici est que l'utilisation de l'opérateur unaire moins, tout comme le reste des opérateurs arithmétiques intégrés, est soumise à promotions intégrales. Étonnamment, le résultat de l'application d'un signe moins unaire à size_t sera toujours size_t et il n'est pas nécessaire de blâmer auto.

Contre-exemple. Dans ce cas, en raison des promotions intégrées, le type de x sera int donc la sortie sera -1:

unsigned short a{1};
auto x{-a};
cout << x << endl;
28
VTT

Votre expression -(sizeof(a)) applique l'unaire - opérateur à une valeur de type non signé. L'opérateur unaire ne transforme pas une valeur intégrale non signée en une valeur signée; il définit plutôt quelle valeur non signée sera le résultat d'une telle opération comme suit (cf. opérateurs arithmétiques unaires sur cppreference.com ):

L'opérateur unaire moins intégré calcule le négatif de son opérande promu. Pour a non signé, la valeur de -a est 2 ^ b -a, où b est le nombre de bits après la promotion.

Par conséquent, même si cela peut être surprenant, auto fonctionne correctement, suite à l'application d'unaire - L'opérateur d'une valeur non signée est toujours une valeur non signée.

21
Stephan Lechner

Le résultat de (unaire) - appliqué à une valeur non signée est non signé, et sizeof renvoie une valeur non signée.

L'opérande de l'opérateur unaire doit avoir un type d'énumération arithmétique ou non étendue et le résultat est la négation de son opérande. La promotion intégrale est effectuée sur des opérandes intégraux ou d'énumération. Le négatif d'une quantité non signée est calculé en soustrayant sa valeur de 2 ^ n, où n est le nombre de bits dans l'opérande promu. Le type du résultat est le type de l'opérande promu.

[expr.unary.op]

Le résultat de sizeof et sizeof... est une constante de type std​::​size_­t

[expr.sizeof]

Pour éviter un comportement défini par l'implémentation, vous devez convertir en int avant d'appliquer le -

Si le type de destination est signé, la valeur est inchangée si elle peut être représentée dans le type de destination; sinon, la valeur est définie par l'implémentation.

[conv.integral]

class A{};

int main() {
    A a;
    auto x = -(int{sizeof(a)});
    cout << x << endl;
    return 0;
}
4
Caleth

Si nous jetons un œil à: https://en.cppreference.com/w/cpp/language/sizeof , le résultat est de type size_t qui est unsigned. Vous devez explicitement le déclarer comme signed int pour autoriser les valeurs négatives.

Au lieu de auto, vous pouvez écrire int qui autorise les valeurs négatives.

1
Giovanni Terlingen