请考虑以下代码:
void foo(unsigned int x)
{
}
int main()
{
foo(-5);
return 0;
}
编译没有问题。像这样的错误可能会导致很多问题并且很难找到。为什么C ++允许这样的转换?
答案 0 :(得分:20)
简短的回答是因为C最初支持这样的转换,并且他们不想破坏C ++中的现有软件。
请注意,有些编译器会对此发出警告。例如,g++ -Wconversion
将警告该构造。
在许多情况下,隐式转换很有用,例如在计算中使用int
时,但最终结果永远不会为负(从算法中得知并且可选地断言)。
编辑:其他可能的解释:请记住,最初的C语言是一种比C ++更宽松的语言。使用K& R样式函数声明,编译器无法检测到这种隐式转换,因此为什么要在语言中限制它。例如,您的代码看起来大致如下:
int foo(x)
unsigned int x
{
}
int main()
{
foo(-5);
return 0;
}
虽然宣言本身就是int foo(x);
编译器实际上依赖程序员将正确的类型传递给每个函数调用,并且在调用站点没有进行转换。然后,当函数实际被调用时,堆栈上的数据(等)被解释为函数声明指示的方式。
一旦编写了依赖于那种隐式转换的代码,即使函数原型添加了实际类型信息,也很难将其从ANSI C中删除。这可能就是为什么它现在仍然存在于C中。然后C ++出现并再次决定不破坏与C的向后兼容性,继续允许这种隐式转换。
答案 1 :(得分:6)
选择。
答案 2 :(得分:4)
@ user168715是对的。 C ++最初设计为C的超集,假装尽可能向后兼容。 “C”的理念是将大部分责任交给程序员,而不是放弃危险的事情。对于C程序员来说,它是天堂,对于Java程序员来说,它是地狱......一个品味问题。
我会挖掘标准,看看它的确切位置,但我现在没有时间做这件事。我会尽快编辑我的答案。
我也同意一些继承的自由可能会导致很难调试的错误,所以我在g ++中添加了说法,你可以打开一个警告来阻止你做这种错误: -Wconversion
旗帜。
警告可能会改变值的隐式转换。这包括 实数和整数之间的转换, 像x是双倍时的abs(x); 签名和签名之间的转换 unsigned,如unsigned ui = -1;和 转换为较小的类型,例如 sqrtf(M_PI)。不要警告明确 像abs((int)x)和ui =的强制转换 (无符号)-1,或者如果值不是 由转换改变,如在abs (2.0)。有关转化的警告 有符号和无符号整数之间 可以使用禁用 -Wno-符号转换。
对于C ++,还要警告用户定义的混乱重载分辨率 转换;和转换将会 从不使用类型转换运算符: 转换为无效,相同的类型,a 基类或对它们的引用。 有关转换的警告 有符号和无符号整数 默认情况下在C ++中禁用,除非 -Wsign-conversion明确启用。
其他编译器可能有类似的标志。
答案 3 :(得分:0)
到原始C标准时,许多(所有?)编译器已经允许转换。根据C的基本原理,似乎很少(如果有的话)讨论是否应该允许这种隐式转换。当C ++出现时,这种隐式转换非常普遍,消除它们会使语言与大量C代码不兼容。它可能使C ++更清洁;它当然使得它的使用更少 - 到了它可能永远不会超越“C with Classes”阶段,甚至那只是一个被忽略的脚注贝尔实验室的历史。
在推广无符号值“小于”int时,沿着这一行的唯一真正问题是“保留值”和“无符号保留”规则。当你(例如)将unsigned short添加到unsigned char时,会出现两者之间的差异。
无符号保留规则表示你将两者都提升为unsigned int。值保留规则表示您将两个值都提升为int
,如果它可以表示原始类型的所有值(例如,8位字符的常见情况,16位短,和32位int)。另一方面,如果int和short都是16位,那么int不能表示unsigned short的所有值,那么你将unsigned short提升为unsigned int(注意它仍然被认为是一个提升,即使它只发生在它真的不促销 - 即两种类型的尺寸相同。)
无论好坏,(并且多次双向争论)委员会都选择了保值而不是未签约的保留促销。但请注意,这会处理相反方向的转换:而不是从有符号转换为无符号转换,而是将无符号转换为有符号转换。
答案 4 :(得分:-1)
因为该标准允许从signed
到unsigned
类型的隐式转换。
同样(int)a + (unsigned)b
会产生unsigned
- 这是c ++标准。