C++11: "сужение преобразования внутри { }" с модулем

Я пытаюсь скомпилировать следующий код с gcc а также C++11 включено:

unsigned int id = 100;
unsigned char array[] = { id % 3, id % 5 };

Я получаю эти предупреждения:

сужение преобразования "(id % 3u)" из "unsigned int" в "unsigned char" внутри { } [-Wararrowing]

посмотреть демо онлайн

Есть ли способ помочь компилятору выяснить, что результат id% 3 вписывается в неподписанный символ?

4 ответа

Решение

В этом конкретном случае решений id const или constexpr исправят проблему:

constexpr unsigned int id = 100;

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

В более общем случае вы также можете использовать static_cast для приведения результата к unsigned char:

{ static_cast<unsigned char>( id % 3), static_cast<unsigned char>( id % 5) }
  ^^^^^^^^^^^                          ^^^^^^^^^^^

Мы можем найти его исключение для константных выражений и сужающих преобразований в проекте стандартного раздела C++. 8.5.4 Инициализация списка, которая говорит:

Сужающее преобразование - это неявное преобразование

и включить следующую пулю (выделено мое):

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

Обратите внимание, что формулировка изменилась с первоначального проекта стандарта C++11 на то, что я цитирую выше, из-за дефектного отчета 1449.

Это особенность C++, что почти все математические операции преобразуют свои аргументы в int,

Вот эскиз нерасширяющегося %mod% оператор:

template<class T, class U,class=void> struct smallest{using type=T;};
template<class T, class U>
struct smallest<T,U,std::enable_if_t<(sizeof(T)>sizeof(U))>>{using type=U;};
template<class T,class U>using smallest_t=typename smallest<T,U>::type;

constexpr struct mod_t {} mod;
template<class LHS>struct half_mod { LHS lhs; };
template<class LHS>
constexpr half_mod<std::decay_t<LHS>> operator%( LHS&& lhs, mod_t ) { return {std::forward<LHS>(lhs)}; }
template<class LHS, class RHS>
constexpr smallest_t<LHS, std::decay_t<RHS>> operator%( half_mod<LHS>&& lhs, RHS&& rhs ) {
  return std::move(lhs.lhs) % std::forward<RHS>(rhs);
}

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

Так id %mod% 3 в конечном итоге char,

Вы можете использовать:

unsigned char array[] = {
    static_cast<unsigned char>(id % 3),
    static_cast<unsigned char>(id % 5)
};

Как id является unsigned int, тип id % 3 также будет unsigned int,

Ваш компилятор предупреждает вас, что unsigned char (по стандарту 8 бит), может быть слишком мало, чтобы получить unsigned int (что по меньшей мере 16 бит по стандарту).

Конечно, вы знаете лучше в этом конкретном случае. использование static_cast<unsigned char>(id % ...) сказать компилятору, что сужающее преобразование безопасно.

Другие вопросы по тегам