int numeral -> Zeigerkonvertierungsregeln

19

Betrachten Sie den folgenden Code.

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

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

MSVC 2017 kompiliert das nicht. Es stellt sich heraus, dass es sich um einen mehrdeutigen überlasteten Anruf handelt, der mit 1-1dem identisch ist 0und daher in diesen konvertiert werden kann double*. Andere Tricks, wie 0x0, 0L, oder static_cast<int>(0), auch nicht arbeiten. Selbst das Deklarieren von a const int Zero = 0und das Aufrufen führen zu f(Zero)demselben Fehler. Es funktioniert nur richtig, wenn Zeronicht const.

Es sieht so aus, als ob das gleiche Problem für GCC 5 und niedriger gilt, jedoch nicht für GCC 6. Ich bin gespannt, ob dies ein Teil des C ++ - Standards, ein bekannter MSVC-Fehler oder eine Einstellung im Compiler ist. Ein flüchtiger Google lieferte keine Ergebnisse.

user1334767
quelle

Antworten:

18

MSVC betrachtet 1-1eine Nullzeigerkonstante. Dies war nach dem Standard für C ++ 03 korrekt, bei dem alle integralen Konstantenausdrücke mit Wert 0Nullzeigerkonstanten waren, wurde jedoch so geändert, dass nur Null-Ganzzahlliterale Nullzeigerkonstanten für C ++ 11 mit CWG-Problem 903 sind . Dies ist eine bahnbrechende Änderung, wie Sie in Ihrem Beispiel sehen und wie auch im Standard dokumentiert, siehe [diff.cpp03.conv] des C ++ 14-Standards (Entwurf N4140).

MSVC wendet diese Änderung nur im Konformitätsmodus an. Ihr Code wird also mit dem /permissive-Flag kompiliert , aber ich denke, die Änderung wurde nur in MSVC 2019 implementiert, siehe hier .

Im Fall von GCC verwendet GCC 5 standardmäßig den C ++ 98-Modus, während GCC 6 und höher standardmäßig den C ++ 14-Modus verwendet, weshalb die Änderung des Verhaltens anscheinend von der GCC-Version abhängt.

Wenn Sie fmit einer Nullzeigerkonstante als Argument aufrufen, ist der Aufruf mehrdeutig, da die Nullzeigerkonstante in einen Nullzeigerwert eines beliebigen Zeigertyps konvertiert werden kann und diese Konvertierung denselben Rang hat wie die Konvertierung von int(oder einem beliebigen ganzzahligen Typ). zu double.

Nussbaum
quelle
-1

Der Compiler funktioniert korrekt gemäß [over.match] und [conv] , genauer [conv.fpint] und [conv.ptr].

Eine Standardkonvertierungssequenz ist [bla bla] Null oder eine [...] Floating-Integral-Konvertierung, Zeigerkonvertierung, [...].

und

Ein Wert eines Integer-Typs oder eines Aufzählungstyps ohne Gültigkeitsbereich kann in einen Wert eines Gleitkomma-Typs konvertiert werden. Das Ergebnis ist wenn möglich genau [bla bla]

und

Eine Nullzeigerkonstante ist ein ganzzahliges Literal mit dem Wert Null oder [...]. Eine Nullzeigerkonstante kann in einen Zeigertyp konvertiert werden. das Ergebnis ist der Nullzeigerwert dieses Typs [bla bla]

Die Überlastungsauflösung besteht nun darin, die beste Übereinstimmung unter allen Kandidatenfunktionen auszuwählen (die als unterhaltsame Funktion nicht einmal am Anrufort verfügbar sein müssen!). Die beste Übereinstimmung ist die mit genauen Parametern oder alternativ mit den geringstmöglichen Conversions. Es können null oder eine Standardkonvertierung auftreten (... für jeden Parameter), und null ist "besser" als eins.

(1-1)ist ein ganzzahliges Literal mit Wert 0.

Sie können das Null-Ganzzahl-Literal mit genau einer Konvertierung in jedes von entweder doubleoder double*(oder nullptr_t) konvertieren. Unter der Annahme, dass mehr als eine dieser Funktionen deklariert ist (wie im Beispiel), gibt es mehr als einen einzelnen Kandidaten, und alle Kandidaten sind gleich gut, es gibt keine beste Übereinstimmung. Es ist mehrdeutig und der Compiler hat Recht, sich zu beschweren.

Damon
quelle
1
Wie ist 1-1ein ganzzahliges Literal ? Es ist ein Ausdruck, der zwei ganzzahlige Literale mit Wert 1und einem -Operator enthält.
Walnuss
@walnut: Sie beziehen sich wahrscheinlich auf die umständliche Formulierung "Folge von Binär-, Oktal-, Ziffern- oder Hexadezimalziffern" . Das ist eine sehr unglückliche Formulierung für etwas ziemlich "Offensichtliches", was auf etwas hindeutet, das nicht der Fall ist (dh das Minuszeichen ausschließt). Mit nur „Ziffern“, und pedantisch gemäß der Definition von „digit“ (einem von 0 ... 9), ist es nicht möglich, hat keine negativen Literale (wie -1). Dies ist jedoch offensichtlich notwendig , da der Standardtyp signiert ist, und es ist nachweislich auch möglich (und allgemein anerkannt).
Damon
1
Ich beziehe mich auf die Grammatik für Ganzzahl-Literal , die im Standard-Link gezeigt wird und nicht übereinstimmt 1-1. C ++ hat keine negativen Ganzzahlliterale. -1ist ein Ausdruck, der aus einem 1ganzzahligen Literal (vom Typ mit Vorzeichen) und einem -unären Minusoperator besteht. Siehe auch den Abschnitt "Notizen" auf cppreference.com .
Walnuss
Es ist sicherlich wahr, dass die Grammatik es nicht hat, aber das ist weitgehend belanglos. Notwendigerweise und per Definition, C ++ sehr viel hat negative Literale, da es wäre denn , Sie explizit append u, Ihre wörtliches sind per Definition unterzeichnet. Vorzeichenbehaftete Typen haben negative Werte (ca. 50% der möglichen Werte sind negativ). Es ist bedauerlich, dass die Grammatik (aus einem Grund, den ich nicht kennen würde) auf diese Weise irreführend ist, und obwohl technisch (laut Grammatik) -1 ein positives Literal ist, das negiert wird, ist es auf alle anderen Arten natürlich ein negatives wörtlich. Ähnlich wie 3 + 4 ist ein Literal.
Damon
Übrigens - ich habe es versucht 0U. Gleicher Fehler. Was ich nicht ausprobiert habe, ist ein enumWert. Vielleicht hätte ein Name die Dinge geändert. Am Ende schrieb ich einen langen Ausdruck mit decltypeund remove_reference.
user1334767