G++ warning: преобразование в uint16_t из int может изменить его значение

По рекомендации пользователя с высоким разрешением 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 (имея дело с операторами | и <<), посмотрел здесь и прочитал Numeric Promotions и Numeric Conversions здесь. Мое понимание состоит в том, что для выполнения математики a и b продвигаются до int (так как это первый тип, который может соответствовать целому диапазону значений uint16_t), выполняется математика, результат записывается назад... кроме результата математики есть int, и запись, возвращаемая на uint16_t, генерирует предупреждение. Консенсус по другим вопросам заключался в основном в том, чтобы отбросить предупреждение, и единственный способ, которым я понял, как это сделать, - b = (uint16_t)(b + a); (или эквивалент b = static_cast<uint16_t>(b + a);).

Не хочу, чтобы этот вопрос становился слишком широким, но предполагая, что мое понимание целочисленных рекламных акций является правильным...

  • Какой лучший способ справиться с этим? Должен ли я избегать выполнения математики по типам, более узким, чем int? Мне кажется довольно странным, что я должен привести арифметический результат, который является тем же типом, что и все операнды (предположим, я ожидаю, что компилятор узнает об этом и подавит предупреждение). Исторически я любил использовать не больше бит, чем мне нужно, и просто дайте компилятору обработать рекламные акции/конверсии/дополнения по мере необходимости.
  • Кто-нибудь часто использует флаг -Wconversion? Уже через пару дней, используя его самостоятельно, я начинаю думать, что его лучший вариант использования - включить его, посмотреть, на что он жалуется, исправить законные жалобы, а затем отменить его. Или, возможно, мое определение "законной жалобы" нуждается в корректировке. Замена всех моих операторов += с помощью произнесенных бросков кажется чем-то неприятным.

У меня возникает соблазн отметить это как c, так как эквивалентный код c, скомпилированный с помощью gcc -Wconversion -std=c11 -O0 myFile.c, выдает то же предупреждение. Но как есть, я использую g++ версию 5.3.1 в поле x86_64 Fedora 23. Пожалуйста, укажите мне обману, если я пропустил это; если единственный ответ/совет - отбросить предупреждение, то это обман.

Ответ 1

Какой лучший способ справиться с этим движением вперед?

-Wno-conversion

Или просто оставьте его неопределенным. Это всего лишь мнение.

По моему опыту, потребность в узкой целочисленной арифметике имеет тенденцию быть довольно редкими, поэтому вы все равно можете сохранить ее для проекта и отключить несколько случаев, когда это бесполезное предупреждение происходит. Однако это, вероятно, сильно зависит от типа вашего проекта, поэтому ваш опыт может отличаться.

Должен ли я избегать выполнения математики по типам, более узким, чем int?

Обычно да; если у вас нет конкретных причин для их использования. "Мне не нужны лишние биты", по моему мнению, не является достаточно определенной причиной. Арифметические операнды в любом случае повышаются до int, и обычно это быстрее и меньше ошибок, чем использование int.

Спустя несколько дней после его использования я начинаю думать, что его лучший вариант использования - включить его, посмотреть, на что он жалуется, исправить законные жалобы и затем отменить его.

Это довольно часто полезный подход к предупреждающим флагам, которые не включены ни в -Wall, ни в -Wextra, например, с префиксом -Wsuggest-. Существует причина, по которой они не включены в "все предупреждения".

Ответ 2

Я думаю, что это можно считать недостатком gcc.

Поскольку этот код не генерирует никаких предупреждений:

int a = ..., b = ...;

a += b;

Этот код также не должен генерировать, потому что семантически они одинаковы (добавляются два одинаковых числа, а результат помещается в переменную того же типа):

short a = ..., b = ...;

a += b;

Но GCC генерирует предупреждение, потому что, как вы говорите, short получает повышение до int. Но версия short не является более опасной, в том смысле, что если добавление переполняется, то поведение определено для обоих случаев (или если используются беззнаковые числа, тогда в обоих случаях может произойти усечение).

Кланг обрабатывает этот случай более разумно и не предупреждает об этом случае. Я думаю, это потому, что на самом деле отслеживает возможную ширину бита (или, может быть, диапазон?) Результата. Так, например, это предупреждает:

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
  • исправить все предупреждения, которые он печатает
  • Вместо этого используйте clang (возможно, для GCC: выключите это предупреждение, а для clang: включите его)

Но не задерживайте дыхание, пока оно не зафиксировано, есть bug об этом, открывшемся в 2009 году.

Примечание:

Исторически мне нравилось использовать не больше бит, чем мне нужно, и просто позволяйте компилятору обрабатывать рекламные акции/конверсии/заполнение по мере необходимости.

Если вы используете более короткие типы для хранения, это нормально. Но обычно нет причин использовать более короткие типы, чем int для арифметики. Это не дает ускорения (даже, это может быть медленнее из-за ненужных масок).