Почему самая отрицательная величина int вызывает ошибку в неоднозначных перегрузках функций?

Я изучаю перегрузку функции на С++ и натолкнулся на это:

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

Из того, что я понял, любое значение, заданное в диапазоне int (в моем случае int равно 4 байтам), вызовет display(int), и любое значение вне этого диапазона будет неоднозначным (поскольку компилятор не может решить, какая функция звонить). Он действителен для полного диапазона значений int, за исключением его минимального значения, т.е. -2147483648, где сбой компиляции с ошибкой

вызов перегруженного display(long int) неоднозначен

Но принимая то же значение в int и печатая значение, получаем 2147483648. Я буквально путаюсь с этим поведением.

Почему такое поведение наблюдается только тогда, когда передается самое отрицательное число? (Поведение такое же, если a short используется с -32768 - фактически, в любом случае, когда отрицательное число и положительное число имеют одинаковое двоичное представление)

Используется компилятор: g++ (GCC) 4.8.5

Ответ 1

Это очень тонкая ошибка. То, что вы видите, является следствием отсутствия отрицательных целых литералов в С++. Если мы посмотрим на [lex.icon], получим, что целочисленный литерал,

целочисленный литерал
        decimal-literal integer-suffix opt
        [...]

может быть десятичным-литералом,

десятичный литерал:
        ненулевая цифра
        decimal-literal opt digit

где цифра [0-9], а ненулевая цифра [1-9], а суффикс par может быть одним из u, u, l, l, ll или ll. Нигде здесь он не включает - как часть десятичного литерала.

В п. 2.2.13 мы также имеем:

Integer литерал представляет собой последовательность цифр, которая не имеет части периода или экспоненты, с необязательным разделением одинарных кавычек, которые игнорируются при определении его значения. Integer литерал может иметь префикс, который указывает его базу и суффикс, который указывает его тип. Лексически первая цифра последовательности цифр является наиболее значимой. Литерал десятичного целого числа (базовая десятка) начинается с цифры, отличной от 0, и состоит из последовательности десятичных цифр.

(акцент мой)

Это означает, что - in -2147483648 является унарным operator -. Это означает, что -2147483648 фактически рассматривается как -1 * (2147483648). Поскольку 2147483648 слишком много для вашего int, оно продвигается до long int, и двусмысленность исходит из того, что не соответствует.

Если вы хотите получить минимальное или максимальное значение для типа переносимым способом, вы можете использовать:

std::numeric_limits<type>::min();  // or max()

Ответ 2

Выражение -2147483648 фактически применяет оператор - к константе 2147483648. На вашей платформе int не может хранить 2147483648, она должна быть представлена ​​более крупным типом. Следовательно, выражение -2147483648 не выводится signed int, а более крупный подписанный тип, вероятно, signed long int.

Поскольку вы не предоставляете перегрузку для long, компилятор вынужден выбирать между двумя перегрузками, которые одинаково (в) действительны. Ваш компилятор должен выпустить ошибку компилятора об неоднозначных перегрузках.

Ответ 3

Расширение ответов других


Чтобы понять, почему OP запутался, сначала: рассмотрим двоичное представление signed int 2147483647 ниже.

Largest signed int




Затем добавьте одно к этому номеру: добавив еще signed int из -2147483648 (который OP хочет использовать) Самый маленький подписанный int



Наконец:, мы можем понять, почему OP путают, когда -2147483648 компилируется в long int вместо signed int, так как он явно подходит для 32 бит.

Но, как указывают текущие ответы, унарный оператор (-) применяется после решения 2147483648, который является long int и НЕ вписывается в 32 бита.