С++ 11: сужающее преобразование внутри {} по модулю

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

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

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

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

см. демонстрацию онлайн

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


person m.s.    schedule 17.11.2014    source источник
comment
static_cast<unsigned char>(id % 3) ?   -  person Borgleader    schedule 17.11.2014
comment
Разве вы не можете сохранить результат в массиве целых чисел? Для чего вы будете его использовать.   -  person Neil Kirk    schedule 17.11.2014


Ответы (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.

person Shafik Yaghmour    schedule 17.11.2014

Причуда 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);
}

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

Таким образом, id %mod% 3 становится char.

person Yakk - Adam Nevraumont    schedule 17.11.2014
comment
Я предполагаю, что есть небольшая ошибка: operator%( LHS&& lhs, mod ) должно быть operator%( LHS&& lhs, mod_t ). Кроме того: это работает только для меня, если я привожу к char: id %mod% (char) 3, иначе 3 также интерпретируется компилятором как int. Как-нибудь обойти это? - person m.s.; 17.11.2014
comment
@РС. ба, я понял, что константа была char, а не int. Как бы ужасно это ни было, '\3' — это символьная константа 3. :/ Возможно, вам лучше всего поставить constexpr на id, если это действительно константа. - person Yakk - Adam Nevraumont; 17.11.2014

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

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

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

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

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

person Bathsheba    schedule 17.11.2014