92

I'm learning about function overloading in C++ and came across this:

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

From what I understood, any value given in the int range (in my case int is 4 byte) will call display(int) and any value outside this range will be ambiguous (since the compiler cannot decide which function to call). It is valid for the complete range of int values except its min value i.e. -2147483648 where compilation fails with the error

call of overloaded display(long int) is ambiguous

But taking the same value to an int and printing the value gives 2147483648. I'm literally confused with this behavior.

Why is this behavior observed only when the most negative number is passed? (The behavior is the same if a short is used with -32768 - in fact, in any case where the negative number and positive number have the same binary representation)

Compiler used: g++ (GCC) 4.8.5

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
infinite loop
  • 1,309
  • 9
  • 19
  • 4
    Int's min value is "throwing a compiler error". What error? You should include it in the question – Justin Aug 02 '17 at 19:17
  • 11
    I see `call of overloaded ‘display(long int)’ is ambiguous`. – crashmstr Aug 02 '17 at 19:18
  • 1
    @crashmstr Yes, the error should be in the question – Justin Aug 02 '17 at 19:18
  • 6
    Not related, but you should update the compiler. There is already GCC 7.1. – HolyBlackCat Aug 02 '17 at 19:19
  • Yeah the same what @crashmstr has mentioned! – infinite loop Aug 02 '17 at 19:19
  • 4
    Here's my guess: `typeof(-2147483648) != int`. The literal is `2147483648`, which is too big for an `int`, so it's a `long`, and it's being negated – Justin Aug 02 '17 at 19:19
  • @HolyBlackCat, the behaviour is same with MSVC++ 14.0 compiler – infinite loop Aug 02 '17 at 19:20
  • perhaps somewhat related: https://stackoverflow.com/questions/8771409/why-is-it-ambiguous-to-call-overloaded-ambiglong-and-ambigunsigned-long-with – underscore_d Aug 02 '17 at 19:23
  • 1
    Possible duplicate of [large negative integer literals](https://stackoverflow.com/questions/8511598/large-negative-integer-literals) – underscore_d Aug 02 '17 at 19:24
  • 1
    @infiniteloop I'm not saying that's because of the compiler. (That's why I said 'not related'.) I just suggest getting an ungrade. – HolyBlackCat Aug 02 '17 at 19:24
  • [Casting minimum 32-bit integer (-2147483648) to float gives positive number (2147483648.0)](https://stackoverflow.com/q/11536389/995714), [(-2147483648> 0) returns true in C++?](https://stackoverflow.com/q/14695118/995714), [Warning: this decimal constant is unsigned only in ISO C90](https://stackoverflow.com/q/9941261/995714) – phuclv Aug 03 '17 at 06:35
  • 1
    [Why does MSVC pick a long long as the type for -2147483648?](https://stackoverflow.com/q/34725215/995714) – phuclv Aug 03 '17 at 06:41
  • 3
    Interestingly, g++ (6.4 and 7.1, at least) don't complain that `int j{-2147483648};` is a narrowing conversion. Almost worth a question in itself, that. It's probably related to allowing (e.g.) `long long` constexpr values such as `2147483647LL` to be narrowed in initialization. – Toby Speight Aug 03 '17 at 07:51

3 Answers3

147

This is a very subtle error. What you are seeing is a consequence of there being no negative integer literals in C++. If we look at [lex.icon] we get that a integer-literal,

integer-literal
        decimal-literal integer-suffixopt
        [...]

can be a decimal-literal,

decimal-literal:
        nonzero-digit
        decimal-literal ’ opt digit

where digit is [0-9] and nonzero-digit is [1-9] and the suffix par can be one of u, U, l, L, ll, or LL. Nowhere in here does it include - as being part of the decimal literal.

In §2.13.2, we also have:

An integer literal is a sequence of digits that has no period or exponent part, with optional separating single quotes that are ignored when determining its value. An integer literal may have a prefix that specifies its base and a suffix that specifies its type. The lexically first digit of the sequence of digits is the most significant. A decimal integer literal (base ten) begins with a digit other than 0 and consists of a sequence of decimal digits.

(emphasis mine)

Which means the - in -2147483648 is the unary operator -. That means -2147483648 is actually treated as -1 * (2147483648). Since 2147483648 is one too many for your int it is promoted to a long int and the ambiguity comes from that not matching.

If you want to get the minimum or maximum value for a type in a portable manner you can use:

std::numeric_limits<type>::min();  // or max()
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 2
    `-2147483647 - 1` would also work without warning as a negative literal expression – Cœur Aug 03 '17 at 05:00
  • 2
    Or `INT_MIN` for the least verbose option. Less generic, though. – MSalters Aug 03 '17 at 08:48
  • @NathanOliver, Can you kindly explain me this case `display(2147483649);`. Why can't it call the unsigned int func in this case? and why it treats the arg `2147483649` as long int instead of unsigned int? – infinite loop Aug 03 '17 at 15:54
  • 2
    @infiniteloop Decimal integer literals go from `int` to `long int` to `long long int`. You wlll never get an unsigned type for a decimal literal unless you use the `u`/`U` suffix. – NathanOliver Aug 03 '17 at 15:57
  • @NathanOliver So, until explicitly given an unsigned int variable or unsigned cast, the unsigned function cannot be called? – infinite loop Aug 03 '17 at 16:08
  • 2
    In this example yes. To call `display(unsigned a)` you need either `display(1234u);` or `display(static_cast(1234));` or `unsigned foo = 1234; display(foo);` – NathanOliver Aug 03 '17 at 16:12
36

The expression -2147483648 is actually applying the - operator to the constant 2147483648. On your platform, int can't store 2147483648, it must be represented by a larger type. Therefore, the expression -2147483648 is not deduced to be signed int but a larger signed type, signed long int.

Since you do not provide an overload for long the compiler is forced to choose between two overloads that are both equally valid. Your compiler should issue a compiler error about ambiguous overloads.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
4

Expanding on others' answers


To clarify why the OP is confused, first: consider the signed int binary representation of 2147483647, below.

Largest signed int




Next, add one to this number: giving another signed int of -2147483648 (which the OP wishes to use) Smallest signed int



Finally: we can see why the OP is confused when -2147483648 compiles to a long int instead of a signed int, since it clearly fits in 32 bits.

But, as the current answers mention, the unary operator (-) is applied after resolving 2147483648 which is a long int and does NOT fit in 32 bits.

bunkerdive
  • 2,031
  • 1
  • 25
  • 28