Warum verursacht der negativste int-Wert einen Fehler bei mehrdeutigen Funktionsüberladungen?

91

Ich lerne etwas über Funktionsüberladung in C ++ und bin auf Folgendes gestoßen:

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);
}

Soweit ich verstanden habe, wird jeder im intBereich angegebene Wert (in meinem Fall int4 Byte) aufgerufen display(int)und jeder Wert außerhalb dieses Bereichs ist mehrdeutig (da der Compiler nicht entscheiden kann, welche Funktion aufgerufen werden soll). Sie gilt für den gesamten Wertebereich mit intAusnahme des Mindestwerts, dh -2147483648wenn die Kompilierung mit dem Fehler fehlschlägt

Anruf überladen display(long int)ist mehrdeutig

Aber den gleichen Wert zu nehmen und den Wert zu intdrucken, ergibt 2147483648. Ich bin buchstäblich verwirrt mit diesem Verhalten.

Warum wird dieses Verhalten nur beobachtet, wenn die negativste Zahl übergeben wird? (Das Verhalten ist das gleiche, wenn a shortverwendet wird -32768- in jedem Fall, wenn die negative und die positive Zahl dieselbe binäre Darstellung haben.)

Verwendeter Compiler: g ++ (GCC) 4.8.5

Endlosschleife
quelle
4
Der minimale Wert von Int ist "Auslösen eines Compilerfehlers". Welcher Fehler? Sie sollten es in die Frage aufnehmen
Justin
11
Ich verstehe call of overloaded ‘display(long int)’ is ambiguous.
Crashmstr
6
Nicht verwandt, aber Sie sollten den Compiler aktualisieren. Es gibt bereits GCC 7.1.
HolyBlackCat
4
Hier ist meine Vermutung : typeof(-2147483648) != int. Das Literal ist 2147483648, was zu groß für ein ist int, also ist es ein long, und es wird negiert
Justin
3
Interessanterweise beklagt sich g ++ (zumindest 6.4 und 7.1) nicht, dass int j{-2147483648};es sich um eine engere Konvertierung handelt. Fast eine Frage an sich wert, das. Es hängt wahrscheinlich damit zusammen, dass (z. B.) long longConstexpr-Werte zugelassen werden 2147483647LL, um sie bei der Initialisierung einzugrenzen .
Toby Speight

Antworten:

145

Dies ist ein sehr subtiler Fehler. Was Sie sehen, ist eine Folge davon, dass es in C ++ keine negativen Ganzzahlliterale gibt. Wenn wir uns [lex.icon] ansehen, erhalten wir ein ganzzahliges Literal .

Ganzzahl-Literal
        Dezimal-Literal Ganzzahl-Suffix opt
        [...]

kann ein Dezimal-Literal sein ,

Dezimal-Literal:
        Nicht-Null-Ziffer
        Dezimal-Literal ' Opt- Ziffer

wo Ziffer ist [0-9]und ungleich Null stelligen ist [1-9]und das Suffix par kann einer der folgenden sein u, U, l, L, ll, oder LL. Nirgendwo hier wird es -als Teil des Dezimalliteral aufgenommen.

In §2.13.2 haben wir auch:

Ein ganzzahliges Literal ist eine Folge von Ziffern ohne Punkt oder Exponententeil, wobei optional einfache Anführungszeichen getrennt werden, die bei der Bestimmung des Werts ignoriert werden. Ein ganzzahliges Literal kann ein Präfix haben, das seine Basis angibt, und ein Suffix, das seinen Typ angibt. Die lexikalisch erste Ziffer der Ziffernfolge ist die signifikanteste. Ein Dezimalzahl- Ganzzahl-Literal (Basis 10) beginnt mit einer anderen Ziffer als 0 und besteht aus einer Folge von Dezimalstellen.

(Hervorhebung von mir)

Was bedeutet, dass das -In -2147483648das Unäre ist operator -. Das heißt, -2147483648wird tatsächlich als behandelt -1 * (2147483648). Da 2147483648es für Sie eins zu viel intist, wird es zu einem befördert long intund die Mehrdeutigkeit ergibt sich daraus, dass es nicht übereinstimmt.

Wenn Sie den minimalen oder maximalen Wert für einen Typ auf tragbare Weise erhalten möchten, können Sie Folgendes verwenden:

std::numeric_limits<type>::min();  // or max()
NathanOliver
quelle
2
-2147483647 - 1würde auch ohne Vorwarnung als negativer wörtlicher Ausdruck funktionieren
Cœur
2
Oder INT_MINfür die am wenigsten ausführliche Option. Weniger allgemein.
MSalters
@NathanOliver, Können Sie erklären mir freundlicherweise diesen Fall display(2147483649);. Warum kann es in diesem Fall nicht die vorzeichenlose int func aufrufen? und warum behandelt es das Argument 2147483649als long int statt unsigned int?
Endlosschleife
2
@infiniteloop Dezimale Ganzzahlliterale gehen von intbis long intnach long long int. Sie erhalten niemals einen vorzeichenlosen Typ für ein Dezimalliteral, es sei denn, Sie verwenden das Suffix u/ U.
NathanOliver
2
In diesem Beispiel ja. Um anzurufen display(unsigned a), benötigen Sie entweder display(1234u);oder display(static_cast<unsigned>(1234));oderunsigned foo = 1234; display(foo);
NathanOliver
36

Der Ausdruck -2147483648wendet den -Operator tatsächlich auf die Konstante an 2147483648. Auf Ihrer Plattform intkann nicht gespeichert werden 2147483648, es muss durch einen größeren Typ dargestellt werden. Daher wird der Ausdruck -2147483648nicht signed intals größerer vorzeichenbehafteter Typ abgeleitet signed long int.

Da Sie longdem Compiler keine Überladung bereitstellen , muss er zwischen zwei Überladungen wählen, die beide gleich gültig sind. Ihr Compiler sollte einen Compilerfehler über mehrdeutige Überladungen ausgeben.

François Andrieux
quelle
4

Die Antworten anderer erweitern


Um zu verdeutlichen, warum das OP verwirrt ist, betrachten Sie zunächst die signed intbinäre Darstellung von 2147483647unten.

Größter signierter Int




Fügen Sie als Nächstes eine zu dieser Nummer hinzu : Geben Sie eine weitere signed intan -2147483648(die das OP verwenden möchte). Kleinste signierte int



Endlich: Wir können sehen, warum das OP beim -2147483648Kompilieren zu a long intanstelle von a verwirrt ist signed int, da es eindeutig in 32 Bit passt.

Wie in den aktuellen Antworten erwähnt, wird der unäre Operator ( -) nach dem Auflösen angewendet , 2147483648der a ist long intund NICHT in 32 Bit passt.

Bunkerdive
quelle