处理编译器关于可能丢失数据的警告的最优雅方法是什么

What's the most elegant way to handle the compiler warning about possible loss of data

本文关键字:警告 是什么 方法 数据 编译器 于可能 处理      更新时间:2023-10-16

在我们的大型C++项目中,我们努力没有警告,但人们往往对此很懒惰。 我不断修复的一件事是来自以下代码的警告:

sizex = sizey = 32 * c_scale;

警告 C4244:"=":从"双精度"转换为"i16",可能会丢失数据

其中 sizex 和 sizey 是 i16 型,c_scale 是双精度型。

所以我不断地将这样的代码行更改为类似

sizex = sizey = i16(32 * c_scale);

使警告消失。

我会说这会使代码的可读性降低,所以我对此并不满意,但在我看来,这比禁用警告要好,也比让一堆警告掩盖可能更严重的警告要好。

有人有更优雅或不同的方式来处理这种情况吗?

作为CppCoreGuidelines的一部分,有一个名为GSL的支持库。它们提供了一种安全的方式来转换类型,如下所示,称为narrow_cast

它的标题可以在这里找到:https://github.com/Microsoft/GSL/blob/master/include/gsl/gsl_util

auto sizex = gsl::narrow<unsigned>(32 * c_scale);

这个想法是,如果结果类型(unsigned这里(不够大(或有符号冲突(,将引发异常。

这是一个运行时检查。如果你想避免运行时检查的成本,你可以构造一个类似的函数,该函数仅在调试构建期间执行检查(当我们未定义时NDEBUG并删除对发布版本的检查。

一些有用的可读性和可维护性指南:

使用标准类型

想象一下,我加入了你的项目。我不知道什么是i16,你链接的任何库也不知道。但我确实知道什么是std::int16_t(在<cstdint>中找到(。图书馆作家也是如此。

隐藏抽象背后的细节

您在此处描述的操作是按某个因子(例如双精度?(缩放整数。细节决定成败。你不能在不丢失数据的情况下将双精度转换回整数,所以你必须恢复到一个转换,这看起来很混乱,会让未来的维护者想知道你在做什么。

因此,让我们构建一个抽象:

inline
auto scale_and_round_down(std::int16_t value, double scale) -> std::uint16_t
{
auto scaled_value = value * scale;  // answer will be a double
return std::int16_t(value * scale); // round down to nearest int
}

现在我们的代码变成:

sizex = sizey = scale_and_round_down(32, c_scale);

毫无疑问地表达了意图。在发布版本中,scale_and_round_down将是内联的。您无需为此类抽象支付任何性能成本。

我不知道你的i16类型是什么,因为它不在 C++11 标准中。我猜你的意思是<cstdint>中的ìnt16_t类型,所以一个有符号的 16 位整数,并且你的sizexsizey都是int16_t类型。

你的c_scale有些double.所以可能是 IEEE754 64 位双精度浮点数。有关详细信息,请参阅 http://floating-point-gui.de/。它有一个53位的尾数。

您如何期望 53 位在 16 位中无损耗地适合(在所有情况下(?鸽子洞原则适用,并立即告诉您,由于 253大于 216,因此情况并非如此。

编译器给你一些警告是正确的。事实上,您可以显式设置一些强制转换以避免该警告

通过解释演员阵容,你只是告诉你的读者你知道这一点。可能,生成的机器代码在有或没有强制转换的情况下根本不会改变。

也许您的特定编译器可能还有其他方法来禁用该警告(可能是某些#pragma(

您可以考虑使用一些更复杂的静态分析技术(可能是Frama-Clang(来进行更语义分析,并证明铸件不会泄漏精度。请注意,此类工具可能很难掌握!

顺便说一句,C++标准一般不会说明警告(它们是"实施质量"的事情(。您可以决定忽略它们(但实际上您不应该(。