Tại sao giá trị int âm nhất lại gây ra lỗi về quá tải hàm không rõ ràng?


91

Tôi đang tìm hiểu về nạp chồng hàm trong C ++ và bắt gặp điều này:

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

Theo những gì tôi hiểu, bất kỳ giá trị nào được cho trong intphạm vi (trong trường hợp của tôi intlà 4 byte) sẽ gọi display(int)và bất kỳ giá trị nào ngoài phạm vi này sẽ không rõ ràng (vì trình biên dịch không thể quyết định gọi hàm nào). Nó hợp lệ cho toàn bộ phạm vi intgiá trị ngoại trừ giá trị nhỏ nhất của nó, tức là -2147483648nơi biên dịch không thành công với lỗi

cuộc gọi quá tải display(long int)là mơ hồ

Nhưng lấy cùng một giá trị cho một intvà in ra giá trị đó 2147483648. Tôi thực sự bối rối với hành vi này.

Tại sao hành vi này chỉ được quan sát khi vượt qua số âm nhất? (Hành vi giống nhau nếu a shortđược sử dụng với -32768- trên thực tế, trong mọi trường hợp khi số âm và số dương có cùng một biểu diễn nhị phân)

Trình biên dịch được sử dụng: g ++ (GCC) 4.8.5


4
Giá trị tối thiểu của Int là "lỗi trình biên dịch". Lỗi gì? Bạn nên đưa nó vào câu hỏi
Justin

11
Tôi hiểu rồi call of overloaded ‘display(long int)’ is ambiguous.
crashmstr

6
Không liên quan, nhưng bạn nên cập nhật trình biên dịch. Đã có GCC 7.1.
HolyBlackCat

4
Dưới đây là dự đoán của tôi: typeof(-2147483648) != int. Hiểu theo nghĩa đen là 2147483648, mà là quá lớn đối với một int, vì vậy đó là một long, và nó đang được phủ nhận
Justin

3
Điều thú vị là g ++ (ít nhất là 6,4 và 7,1) không phàn nàn rằng đó int j{-2147483648};là một chuyển đổi thu hẹp. Gần như giá trị một câu hỏi tự nó, điều đó. Nó có thể liên quan đến việc cho phép (ví dụ) long longcác giá trị constexpr chẳng hạn như 2147483647LLđược thu hẹp trong quá trình khởi tạo.
Toby Speight

Câu trả lời:


145

Đây là một lỗi rất tinh vi. Những gì bạn đang thấy là hệ quả của việc không có các ký tự nguyên âm trong C ++. Nếu chúng ta nhìn vào [lex.icon], chúng ta nhận được rằng một số nguyên-chữ ,

số nguyên-chữ
        thập phân-theo nghĩa đen số nguyên-hậu tố chọn
        [...]

có thể là một chữ thập phân ,

số thập phân-đen:
        nonzero chữ số
        thập phân-đen ' từ chối chữ số

nơi chữ số[0-9]nonzero chữ số[1-9]và mệnh hậu tố có thể là một trong những u, U, l, L, ll, hoặc LL. Không nơi nào trong đây nó bao gồm -như là một phần của chữ thập phân.

Trong §2.13.2, chúng ta cũng có:

Một chữ số nguyên là một chuỗi các chữ số không có dấu chấm hoặc phần số mũ, với các dấu nháy đơn phân tách tùy chọn bị bỏ qua khi xác định giá trị của nó. Một ký tự số nguyên có thể có một tiền tố chỉ định cơ sở của nó và một hậu tố chỉ định kiểu của nó. Chữ số đầu tiên theo từ vựng của dãy chữ số là chữ số có nghĩa nhất. Một chữ số nguyên thập phân (cơ số mười) bắt đầu bằng một chữ số khác 0 và bao gồm một chuỗi các chữ số thập phân.

(nhấn mạnh của tôi)

Có nghĩa là -trong -2147483648là một ngôi operator -. Điều đó có nghĩa -2147483648là thực sự được coi là -1 * (2147483648). Vì 2147483648là một quá nhiều đối với bạn intnên nó được thăng cấp thành a long intvà sự mơ hồ đến từ việc không khớp.

Nếu bạn muốn nhận giá trị tối thiểu hoặc tối đa cho một loại theo cách di động, bạn có thể sử dụng:

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

2
-2147483647 - 1cũng sẽ hoạt động mà không có cảnh báo như một biểu thức nghĩa đen tiêu cực
Cœur

2
Hoặc INT_MINđối với tùy chọn ít dài dòng nhất. Tuy nhiên, ít chung chung hơn.
MSalters

@NathanOliver, Bạn vui lòng giải thích giúp mình trường hợp này display(2147483649);. Tại sao nó không thể gọi func int unsigned trong trường hợp này? và tại sao nó lại coi đối số 2147483649là long int thay vì int không dấu?
vòng lặp vô hạn

2
@infiniteloop Decimal số nguyên literals đi từ intđể long intđến long long int. Bạn sẽ không bao giờ nhận được kiểu không dấu cho một chữ thập phân trừ khi bạn sử dụng hậu tố u/ U.
NathanOliver

2
Trong ví dụ này có. Để gọi display(unsigned a)bạn cần một trong hai display(1234u);hoặc display(static_cast<unsigned>(1234));hoặcunsigned foo = 1234; display(foo);
NathanOliver

36

Biểu thức -2147483648thực sự đang áp dụng -toán tử cho hằng số 2147483648. Trên nền tảng của bạn, intkhông thể lưu trữ 2147483648, nó phải được đại diện bởi một loại lớn hơn. Do đó, biểu thức -2147483648không được suy ra là signed intmà là một kiểu có dấu lớn hơn signed long int,.

Vì bạn không cung cấp quá tải cho longtrình biên dịch nên buộc phải chọn giữa hai quá tải có giá trị như nhau. Trình biên dịch của bạn sẽ gặp lỗi trình biên dịch về các quá tải không rõ ràng.


4

Mở rộng câu trả lời của người khác


Để làm rõ lý do tại sao OP bị nhầm lẫn, trước tiên : hãy xem xét signed intbiểu diễn nhị phân của 2147483647, bên dưới.

Số nguyên ký lớn nhất




Tiếp theo, thêm một vào số này : đưa ra một signed inttrong số khác -2147483648(mà OP muốn sử dụng) Int có ký nhỏ nhất



Cuối cùng: chúng ta có thể thấy lý do tại sao OP bị nhầm lẫn khi -2147483648biên dịch thành a long intthay vì a signed int, vì nó rõ ràng phù hợp với 32 bit.

Nhưng, như các câu trả lời hiện tại đã đề cập, toán tử một ngôi ( -) được áp dụng sau khi giải quyết 2147483648là a long intvà KHÔNG phù hợp với 32 bit.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.