Pourquoi la valeur int la plus négative provoque-t-elle une erreur concernant les surcharges de fonctions ambiguës?

91

J'apprends la surcharge de fonctions en C ++ et je suis tombé sur ceci:

void display(int a)
{
    cout << "int" << endl;
}

void display(unsigned a)
{
    cout << "unsigned" << endl;
}

int main()
{
    int i = -2147483648;
    cout << i << endl; //will display -2147483648
    display(-2147483648);
}

D'après ce que j'ai compris, toute valeur donnée dans la intplage (dans mon cas intest de 4 octets) sera appelée display(int)et toute valeur en dehors de cette plage sera ambiguë (puisque le compilateur ne peut pas décider quelle fonction appeler). Il est valide pour toute la plage de intvaleurs sauf sa valeur minimale, c'est-à-dire -2147483648lorsque la compilation échoue avec l'erreur

l'appel de surcharge display(long int)est ambigu

Mais prendre la même valeur à un intet imprimer la valeur donne 2147483648. Je suis littéralement confus avec ce comportement.

Pourquoi ce comportement n'est-il observé que lorsque le nombre le plus négatif est passé? (Le comportement est le même si a shortest utilisé avec -32768- en fait, dans tous les cas où le nombre négatif et le nombre positif ont la même représentation binaire)

Compilateur utilisé: g ++ (GCC) 4.8.5

boucle infinie
la source
4
La valeur minimale d'Int est "lançant une erreur de compilation". Quelle erreur? Vous devriez l'inclure dans la question
Justin
11
Je vois call of overloaded ‘display(long int)’ is ambiguous.
crashmstr
6
Non lié, mais vous devez mettre à jour le compilateur. Il existe déjà GCC 7.1.
HolyBlackCat
4
Voici ma proposition: typeof(-2147483648) != int. Le littéral est 2147483648, qui est trop grand pour un int, donc c'est un long, et il est annulé
Justin
3
Il est intéressant de noter que g ++ (au moins 6.4 et 7.1) ne se plaint pas d' int j{-2147483648};une conversion restrictive. Cela vaut presque une question en soi. C'est probablement lié au fait d'autoriser (par exemple) les long longvaleurs constexpr telles que 2147483647LLd'être réduites lors de l'initialisation.
Toby Speight

Réponses:

145

C'est une erreur très subtile. Ce que vous voyez est une conséquence de l'absence de littéraux entiers négatifs en C ++. Si nous regardons [lex.icon], nous obtenons un entier-littéral ,

entier-littéral
        décimal-littéral entier-suffixe opt
        [...]

peut être un littéral décimal ,

decimal-literal:
        chiffre différent de zéro
        décimal-littéral ' opt digit

chiffre est [0-9]et chiffres d' une valeur non nulle est [1-9]et par suffixe peut être l' un u, U, l, L, ll, ou LL. Nulle part ici cela ne fait -partie du littéral décimal.

Au §2.13.2, nous avons également:

Un littéral entier est une séquence de chiffres sans point ni partie d'exposant, avec des guillemets simples de séparation facultatifs qui sont ignorés lors de la détermination de sa valeur. Un littéral entier peut avoir un préfixe qui spécifie sa base et un suffixe qui spécifie son type. Le premier chiffre lexicalement de la séquence de chiffres est le plus significatif. Un littéral entier décimal (base dix) commence par un chiffre autre que 0 et se compose d'une séquence de chiffres décimaux.

(c'est moi qui souligne)

Ce qui signifie que le -in -2147483648est unaire operator -. Cela signifie -2147483648est réellement traité comme -1 * (2147483648). Puisqu'il 2147483648y en a un de trop pour votre, intil est promu à un long intet l'ambiguïté vient de ce qui ne correspond pas.

Si vous souhaitez obtenir la valeur minimale ou maximale d'un type de manière portable, vous pouvez utiliser:

std::numeric_limits<type>::min();  // or max()
NathanOliver
la source
2
-2147483647 - 1fonctionnerait également sans avertissement comme une expression littérale négative
Cœur
2
Ou INT_MINpour l'option la moins verbeuse. Moins générique, cependant.
MSalters
@NathanOliver, pouvez-vous bien m'expliquer ce cas display(2147483649);. Pourquoi ne peut-il pas appeler la fonction int unsigned dans ce cas? et pourquoi il traite l'argument 2147483649comme long int au lieu de unsigned int?
boucle infinie
2
@infiniteloop Les littéraux entiers décimaux vont de intà long intà long long int. Vous n'obtiendrez jamais un type non signé pour un littéral décimal sauf si vous utilisez le suffixe u/ U.
NathanOliver
2
Dans cet exemple oui. Pour appeler, display(unsigned a)vous avez besoin de display(1234u);ou display(static_cast<unsigned>(1234));ouunsigned foo = 1234; display(foo);
NathanOliver
36

L'expression -2147483648applique en fait l' -opérateur à la constante 2147483648. Sur votre plateforme, intimpossible de stocker 2147483648, il doit être représenté par un type plus grand. Par conséquent, l'expression -2147483648ne se déduit pas être , signed intmais un type plus grand signé, signed long int.

Puisque vous ne fournissez pas de surcharge, longle compilateur est obligé de choisir entre deux surcharges qui sont toutes deux également valides. Votre compilateur doit émettre une erreur de compilation concernant les surcharges ambiguës.

François Andrieux
la source
4

Élargir les réponses des autres


Pour clarifier pourquoi l'OP est confus, tout d'abord : considérez la signed intreprésentation binaire de 2147483647ci-dessous.

Le plus grand int signé




Ensuite, ajoutez un à ce numéro : en donnant un autre signed intde -2147483648(que l'OP souhaite utiliser) Le plus petit int signé



Enfin: nous pouvons voir pourquoi l'OP est confus lors de la -2147483648compilation en a au long intlieu de a signed int, car il tient clairement en 32 bits.

Mais, comme le mentionnent les réponses actuelles, l'opérateur unaire ( -) est appliqué après la résolution de 2147483648ce qui est a long intet ne tient PAS dans 32 bits.

plongée au bunker
la source