根据高级别SO用户的建议,我最近开始使用代码库中的-Wconversion
标志进行编译。这已经产生了很多警告,其中一些是合法的(例如,不必要地添加signed
和unsigned
类型),但也有一些头颅,如下所示:
#include <cstdint>
int main()
{
uint16_t a = 4;
uint16_t b = 5;
b += a;
return 0;
}
当我使用g++ -Wconversion -std=c++11 -O0 myFile.cpp
进行编译时,我得到了
warning: conversion to 'uint16_t {aka short unsigned int}' from 'int' may alter its value [-Wconversion]
b += a;
^
我已经在SO上讨论了一些类似的问题(处理|
和<<
运算符),看了here,并阅读了数字促销和数字转化部分here。我的理解是,为了进行数学计算,a
和b
被提升为int
(因为那是第一种可以适合整个uint16_t
的类型值范围),执行数学,结果被写回...除了数学结果是int
,并将其写回uint16_t
生成警告。其他问题的共识基本上就是抛弃了警告,我唯一知道如何做到这一点的方法是b = (uint16_t)(b + a);
(或等效的b = static_cast<uint16_t>(b + a);
)。
不要让这个问题过于宽泛,但假设我对整数促销的理解是正确的......
int
更窄的类型进行数学运算吗?对我来说似乎很奇怪,我必须转换一个与所有操作数相同类型的算术结果(我希望编译器能够识别并抑制警告)。从历史上看,我不想使用比我需要的更多的位,只需让编译器根据需要处理促销/转换/填充。-Wconversion
标志吗?在我自己使用它几天之后,我开始认为它的最佳用例是打开它,查看它抱怨的内容,修复合法的投诉,然后将其关闭。或者也许是我对合法投诉的定义&#34;需要重新调整。用拼写出来的演员替换我的所有+=
运算符似乎比任何事都更令人讨厌。我也很想将此标记为c
,因为使用c
编译的等效gcc -Wconversion -std=c11 -O0 myFile.c
代码会产生完全相同的警告。但就是这样,我在x86_64 Fedora 23盒子上使用g++
版本5.3.1。如果我错过了,请指出我的欺骗行为;如果这里唯一的答案/建议是抛弃警告,那么这就是一个骗局。
答案 0 :(得分:4)
处理这种前进的最佳方法是什么?
-Wno-conversion
或者只是不指明。不过,这只是一种意见。
根据我的经验,对窄整数运算的需求往往很少,因此您仍然可以为项目保留它,并禁用发生此无用警告的少数情况。但是,这可能在很大程度上取决于您的项目类型,因此您的体验可能会有所不同。
我应该避免对比int更窄的类型进行数学运算吗?
通常是;除非您有特定的理由使用它们。 “我不需要额外的位”在我看来并不是一个特定的理由。无论如何,算术操作数被提升为int
,并且使用int
通常更快,更不容易出错。
在我自己使用它几天之后,我开始认为它的最佳用例是打开它,查看它抱怨的内容,修复合法的投诉,然后将其关闭。
对于警告标记,这通常是一种有用的方法,它既不包含-Wall
也不包含-Wextra
,例如-Wsuggest-
前缀的标记。有一个原因,他们不包括在“所有警告”中。
答案 1 :(得分:3)
我认为这可以被认为是gcc的缺点。
由于此代码未生成任何警告:
int a = ..., b = ...;
a += b;
此代码也不应该生成,因为在语义上它们是相同的(添加了两个相同类型的数字,并将结果放入相同类型的变量中):
short a = ..., b = ...;
a += b;
但GCC会发出警告,因为正如您所说,short
会被提升为int
。但是short
版本与int
版本相比并不危险,因为如果添加溢出,那么行为是针对short
情况的实现定义的,并且对于int
情况未定义(或者如果使用无符号数字,则在两种情况下都可能发生截断)。
Clang更智能地处理这种情况,并不会对此案例发出警告。我认为这是因为它实际上跟踪了结果的可能位宽(或可能是范围?)。因此,例如,这会警告:
int a = ...;
short b = a;
但这并不是(但GCC警告说):
int a = ...;
short b = a&0xf; // there is a conversion here, but clang knows that only 4 bits are used, so it doesn't warn
因此,在GCC具有更智能-Wconversion
之前,您的选择是:
-Wconversion
但不要屏住呼吸,直到它被修复,有一个bug关于此,于2009年开放。
注意:
从历史上看,我不想使用比我需要的更多的位,只需让编译器根据需要处理促销/转换/填充。
如果您使用较短的类型进行存储,那就没问题了。但通常情况下,没有理由使用比int
更短的类型来进行算术运算。它没有加速(即使它可能更慢,因为不必要的掩码)。