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

时间:2018-10-03 07:14:06

标签: c++ compiler-warnings

在我们的大型C ++项目中,我们力争没有警告,但是人们对此常常很懒。我一直在修正的一件事是来自如下代码的警告:

sizex = sizey = 32 * c_scale;

给予

  

警告C4244:'=':从'double'转换为'i16',可能会丢失数据

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

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

sizex = sizey = i16(32 * c_scale);

使警告消失。

我想说这会使代码的可读性降低,所以我对此并不满意,但我认为它比禁用警告要好,并且比让大量警告掩盖可能更严重的警告要好。 / p>

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

3 个答案:

答案 0 :(得分:3)

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

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

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

想法是,如果结果类型(此处为unsigned)不够大(或违反符号),则会引发异常。

这是运行时检查。如果要避免运行时检查的开销,可以构造一个类似的函数,该函数仅在 debug 构建期间执行检查(当NDEBUG未定义时),并删除对发布版本。

答案 1 :(得分:3)

关于可读性和可维护性的一些有用准则:

使用标准类型

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

隐藏抽象背后的细节

您在此处描述的操作是将整数按一定比例缩放(例如double?)。细节在于魔鬼。您不能在不丢失数据的情况下将双精度型转换回整数,因此必须恢复为强制转换,这看起来很杂乱,并使未来的维护人员想知道您在做什么。

所以让我们建立一个抽象:

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。您不会为这种抽象支付任何性能费用。

答案 2 :(得分:2)

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

您的c_scale是一些double。因此可能是IEEE754 64位双精度浮点数。有关更多信息,请参见http://floating-point-gui.de/。它有一个53位的尾数。

您如何期望16位中的53位适合无损失(在所有情况下)? pigeonhole principle适用并立即告诉您,由于2 53 大于2 16 ,因此并非如此。

编译器向您发出警告是正确的。确实,您可以明确强制转换以避免该警告

通过解释演员表,您只是告诉您的读者您已经知道这一点。不管有没有强制转换,生成的机器代码都不会改变。

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

您可以考虑使用一些更复杂的静态分析技术(也许是Frama-Clang)来进行语义分析,并向您证明强制转换不会泄漏精度。当心,这样的工具可能很难掌握!

顺便说一句,C ++标准在一般情况下对警告的内容不多(它们是“实现质量”的东西)。您可以决定忽略它们(但实际上不应这样做)。