int numeral -> règles de conversion du pointeur

19

Considérez le code suivant.

void f(double p) {}
void f(double* p) {}

int main()
{ f(1-1); return 0; }

MSVC 2017 ne compile pas cela. Il suppose qu'il y a un appel surchargé ambigu, comme 1-1c'est le même 0et donc peut être converti en double*. D'autres astuces, comme 0x0, 0Lou static_cast<int>(0), ne fonctionnent pas non plus. Même déclarer un const int Zero = 0et appeler f(Zero)produit la même erreur. Cela ne fonctionne correctement que siZero n'est pas le cas const.

Il semble que le même problème s'applique à GCC 5 et inférieur, mais pas à GCC 6. Je suis curieux de savoir si cela fait partie de la norme C ++, d'un bogue MSVC connu ou d'un paramètre du compilateur. Un rapide Google n'a pas donné de résultats.

user1334767
la source

Réponses:

18

MSVC considère 1-1comme une constante de pointeur nul. Cela était correct par la norme pour C ++ 03, où toutes les expressions constantes intégrales avec valeur 0étaient des constantes de pointeur nul, mais elle a été modifiée de sorte que seuls les littéraux entiers nuls soient des constantes de pointeur nul pour C ++ 11 avec CWG issue 903 . Il s'agit d'un changement de rupture, comme vous pouvez le voir dans votre exemple et comme cela est également documenté dans la norme, voir [diff.cpp03.conv] de la norme C ++ 14 (projet N4140).

MSVC applique ce changement uniquement en mode de conformité. Votre code se compilera donc avec le /permissive-drapeau, mais je pense que le changement a été implémenté uniquement dans MSVC 2019, voir ici .

Dans le cas de GCC, GCC 5 par défaut en mode C ++ 98, tandis que GCC 6 et versions ultérieures par défaut en mode C ++ 14, c'est pourquoi le changement de comportement semble dépendre de la version de GCC.

Si vous appelez f avec une constante de pointeur nul comme argument, l'appel est ambigu, car la constante de pointeur nul peut être convertie en une valeur de pointeur nul de tout type de pointeur et cette conversion a le même rang que la conversion de int(ou tout type intégral) à double.

noyer
la source
-1

Le compilateur fonctionne correctement, conformément à [over.match] et [conv] , plus précisément [conv.fpint] et [conv.ptr].

Une séquence de conversion standard est [bla bla] zéro ou une [...] conversion intégrale flottante, conversions de pointeurs, [...].

et

Une valeur d'un type entier ou d'un type d'énumération non étendue peut être convertie en une valeur d'un type à virgule flottante. Le résultat est exact si possible [bla bla]

et

Une constante de pointeur nul est un littéral entier de valeur zéro ou [...]. Une constante de pointeur nul peut être convertie en un type de pointeur; le résultat est la valeur du pointeur nul de ce type [bla bla]

Maintenant, la résolution de surcharge consiste à choisir la meilleure correspondance entre toutes fonctions candidates (qui, en tant que fonctionnalité amusante, n'ont même pas besoin d'être accessibles sur le lieu de l'appel!). La meilleure correspondance est celle avec les paramètres exacts ou, le cas échéant, le moins de conversions possibles. Zéro ou une conversion standard peut se produire (... pour chaque paramètre), et zéro est "meilleur" qu'un.

(1-1) est un littéral entier avec une valeur 0 .

Vous pouvez convertir le littéral entier zéro en chacun de doubleou double*(ou nullptr_t), avec exactement une conversion. Donc, en supposant que plusieurs de ces fonctions sont déclarées (comme c'est le cas dans l'exemple), il existe plus d'un seul candidat, et tous les candidats sont également bons, il n'y a pas de meilleure correspondance. C'est ambigu, et le compilateur a raison de se plaindre.

Damon
la source
1
Comment est 1-1un littéral entier ? Il s'agit d'une expression contenant deux littéraux entiers avec une valeur 1et un -opérateur.
noyer
@walnut: Vous vous référez probablement à la formulation maladroite "séquence de chiffres binaires, chiffres octaux, chiffres ou chiffres hexadécimaux" . C'est un libellé très malheureux pour quelque chose d'assez "évident", qui suggère quelque chose qui n'est pas le cas (c'est-à-dire en excluant le caractère moins). Avec seulement « chiffres », et pédante par la définition de « chiffres » (un des 0 ... 9), il est impossible d'avoir des littéraux négatifs (tels que -1). Ce qui, étant donné que le type par défaut est signé , est évidemment nécessaire, et il est manifestement possible (et universellement accepté) aussi.
Damon
1
Je fais référence à la grammaire pour le littéral entier montrée dans le lien standard, qui ne correspond pas 1-1. C ++ n'a pas de littéraux entiers négatifs. -1est une expression composée d'un 1littéral entier (de type signé) et d'un -opérateur moins unaire. Voir également la section "Notes" sur cppreference.com .
noyer
Il est certainement vrai que la grammaire ne l'a pas, mais c'est largement sans conséquence. Par nécessité et par définition, C ++ très bien ne littéraux négatifs, car à moins que vous explicitement append u, votre littéral est, par définition, signé. Les types signés ont des valeurs négatives (environ 50% des valeurs possibles sont négatives). Il est malheureux que la grammaire (pour une raison que je ne connais pas) soit trompeuse de cette manière, et bien que techniquement (selon la grammaire) -1 soit un littéral positif, nié, par tous les autres moyens, c'est bien sûr un négatif littéral. Tout comme 3 + 4 est un littéral.
Damon
Au fait - j'ai essayé 0U. Même problème. Ce que je n'ai pas essayé, c'est une enumvaleur. Peut-être qu'une personne nommée aurait changé les choses. J'ai fini par écrire une longue expression avec decltypeet remove_reference.
user1334767