C++11: “narrowing conversion inside { }” with modulus

六月ゝ 毕业季﹏ 提交于 2019-11-27 16:13:10

In this specific case making id const or constexpr will fix the problem:

constexpr unsigned int id = 100;

since there is an exception for the case where you have a constant expression whose result after conversion will fit into the target type.

In the more general case you may also use static_cast to cast the result to unsigned char:

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

We can find he exception for constant expressions and narrowing conversions in the draft C++ standard section 8.5.4 List-initialization which says:

A narrowing conversion is an implicit conversion

and include the following bullet (emphasis mine):

  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type.

Note, the wording changed from the original draft C++11 standard to what I quote above due to defect report 1449.

It is a quirk of C++ that almost all mathematical operations convert their arguments to int.

Here is a sketch of a non-widening %mod% operator:

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);
}

The result of a mod b should be the smallest of the two types, as it cannot be larger. Possibly some work should be done for signed/unsigned, but I'll punt and take the first.

So id %mod% 3 ends up being char.

You may use:

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

As id is an unsigned int, the type of id % 3 will also be an unsigned int.

Your compiler is helpfully warning you that unsigned char (which is 8 bits by the standard), might be too small to receive the unsigned int (which is at least 16 bits by the standard).

Of course you know better in this particular instance. Use static_cast<unsigned char>(id % ...) to tell the compiler that the narrowing conversion is safe.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!