C++隐式数字类型降级

C++ implicit numeric type demoting

本文关键字:类型 降级 数字 C++      更新时间:2023-10-16

最近,我注意到 C/C++ 似乎非常允许进行数值类型转换,因为它隐式地将双精度转换为 int。

测试:

环境: cpp.shStandard C++ 14Compilation warnings all set

法典:

int intForcingFunc(double d) {
    return d;                       // this is allowed
}
int main() {
    double d = 3.1415;
    double result = intForcingFunc(d);
    printf("intForcingFunc result = %fn", result);
    int localRes = d;               // this is allowed
    printf("Local result = %dn", localRes);
    int staticCastRes = static_cast<int>(d);                // also allowed
    printf("Static cast result = %dn", staticCastRes);
}

编译期间没有警告问题。

文档不切地提到了主题,但忽略了问题的确切情况:

C++是一种强类型语言。许多转换(尤其是那些暗示对值的不同解释的转换(需要显式转换,C++称为类型转换。

我也尝试过使用托管语言 (C#(,但不允许所有这些情况(如预期的那样(:

static int intForcingFunc(double d)
{
    // Not legal: Cannot implicitly convert type 'double' to 'int'
    // return d;
    return Convert.ToInt32(d);
}
static void Main(string[] args)
{
    double d = 3.1415;
    double result = intForcingFunc(d);
    Console.WriteLine("intForcingFunc result = " + result);
    // Not legal: Cannot implicitly convert type 'double' to 'int'
    // int localRes = d;
    int localRes = (int)d;
    Console.WriteLine("local result = " + result);
    Console.ReadLine();
}

为什么在强类型语言中允许此行为?在大多数情况下,这是不希望的行为。这背后的一个原因似乎是缺乏算术溢出检测。

不幸的是,这种行为是从 C 继承而来的,众所周知,C 在这些事情上"信任程序员"。

隐式浮点到整数转换的确切警告标志是 -Wfloat-conversion ,这也由 -Wconversion 启用。由于某种未知的原因,-Wall-Wextra-pedantic(cpp.sh 提供(不包括这些标志。

如果您使用 Clang,您可以-Weverything它启用几乎所有警告。如果使用 GCC,则必须显式启用 -Wfloat-conversion-Wconversion,以便在执行此类转换时收到警告(以及您希望启用的其他有用标志(。

如果需要,您可以将其转换为错误,例如 -Werror-conversion .


C++11 甚至引入了一种全新的更安全的初始化语法,称为统一初始化,您可以使用它来获取示例中隐式转换的警告,而无需启用任何编译器警告:

int intForcingFunc(double d) {
    return {d};  // warning: narrowing conversion of 'd' from 'double' to 'int' inside { }
}
int main() {
    double d{3.1415};  // allowed
    int localRes{d};  // warning: narrowing conversion of 'd' from 'double' to 'int' inside { }
}

您没有指定正在使用的编译器,但实际上您可能没有启用所有警告。 它背后有一个故事,但净效应是g++ -Wall实际上并没有启用所有警告(甚至没有关闭(。 其他的(例如 clang++(,为了与 g++ 直接替换兼容,必须做同样的事情。

这里有一篇关于为 g++ 设置强烈警告的好文章: https://stackoverflow.com/a/9862800/1541330

如果您使用的是 clang++,事情对您来说会容易得多:尝试使用 -Weverything . 它完全符合您的期望(打开每个警告(。 您可以添加-Werror编译器随后会将发生的任何警告视为编译时错误。 如果您现在看到要禁止显示的警告(错误(,只需将-Wno-<warning-name>添加到命令中即可(例如。 -Wno-c++98-compat (。

现在,每当发生隐式缩小转换(您未明确要求可能丢失数据的转换(时,编译器都会向您发出警告。 如果您希望进行缩小转换范围,则必须使用适当的转换,例如:

int intForcingFunc(double d) {
    return static_cast<int>(d);   //cast is now required
}