为什么最负的int值会导致有关歧义函数重载的错误?


91

我正在学习C ++中的函数重载,并遇到了以下问题:

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


4
Int的最小值是“引发编译器错误”。什么错 您应该将其添加到问题中
贾斯汀·贾斯丁(Justin)

11
我知道了call of overloaded ‘display(long int)’ is ambiguous
crashmstr

6
无关,但是您应该更新编译器。已经有GCC 7.1。
HolyBlackCat

4
这是我的猜想:typeof(-2147483648) != int。字面量是2147483648,对于来说太大了int,所以它是long,并且被否定了
贾斯汀

3
有趣的是,g ++(至少为6.4和7.1)不会抱怨int j{-2147483648};转换范围越来越窄。几乎值得一个问题。这可能与允许(例如)long longconstexpr值(例如2147483647LL在初始化时变窄)有关。
Toby Speight

Answers:


145

这是一个非常微妙的错误。您所看到的是C ++中没有负整数文字的结果。如果我们查看[lex.icon],我们会得到一个整数字面量

整数字面量
        十进制字面量整数后缀opt
        [...]

可以是一个十进制文字

十进制数:
        非零数字
        十进制数' opt digit

其中数字[0-9]非零位数[1-9]和后缀面值可以是一个uUlLll,或LL。这里没有任何地方包含-作为十进制文字的一部分。

在第2.13.2节中,我们还有:

一个整数文字是不具有周期或指数部分数字序列,条件是确定其值时被忽略可选分离单引号。整数文字可以具有指定其基数的前缀和指定其类型的后缀。数字序列的词法上的第一个数字是最高有效的。十进制整数文字(基10)开始于0以外的位数和由十进制数字序列的。

(强调我的)

这意味着-in -2147483648是一元的operator -。这意味着-2147483648实际上被视为-1 * (2147483648)。由于2147483648对您int来说太多了,它被提升为一个long int,而歧义来自于不匹配。

如果要以可移植的方式获取类型的最小值或最大值,可以使用:

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

2
-2147483647 - 1也没有警告的工作作为一个负文字表达式
心教堂

2
INT_MIN最少详细的选项。不过,通用性较差。
MSalters

@NathanOliver,您能为我解释一下这种情况吗display(2147483649);?为什么在这种情况下不能调用unsigned int函数?为什么将arg 2147483649视为long int而不是unsigned int?
无限循环,

2
@infiniteloop十进制整数从去intlong intlong long int。除非使用u/ U后缀,否则您将永远不会获得十进制文字的无符号类型。
NathanOliver '17

2
在此示例中,是。要调用display(unsigned a)您需要用display(1234u);display(static_cast<unsigned>(1234));unsigned foo = 1234; display(foo);
NathanOliver

36

该表达式-2147483648实际上是将-运算符应用于常数2147483648。在您的平台上,int无法存储2147483648,它必须由较大的类型表示。因此,表达式-2147483648不能推论为signed int更大的带符号类型signed long int

由于未提供重载,long因此编译器被迫在两个都同样有效的重载之间进行选择。您的编译器应发出有关模棱两可的重载的编译器错误。


4

扩展他人的答案


要弄清为什么混淆了OP,首先:考虑下面的的signed int二进制表示形式2147483647

最大签名整数




接下来,添加一个到这个号码:给另一个signed int-2147483648(其中OP希望使用) 最小的有符号整数



最后:我们可以看到为什么OP -2147483648编译为a long int而不是a 时会感到困惑signed int,因为它显然适合32位。

但是,如当前答案所述,一元运算符(-解析应用2147483648a long int且不适合32位。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.