我尝试在启用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 ?
答案 0 :(得分:9)
在这种特定情况下,id
const 或constexpr将解决问题:
constexpr unsigned int id = 100;
因为你有一个常量表达式的情况有一个例外,它的转换结果将适合目标类型。
在更一般的情况下,您也可以使用static_cast将结果转换为unsigned char:
{ static_cast<unsigned char>( id % 3), static_cast<unsigned char>( id % 5) }
^^^^^^^^^^^ ^^^^^^^^^^^
我们可以在draft C++ standard部分8.5.4
列表初始化中找到常量表达式的异常并缩小转化次数,其中包含:
缩小转化是隐式转化
并包含以下项目符号(强调我的):
- 从整数类型或未范围的枚举类型到不能表示原始类型的所有值的整数类型,除外 其中source是一个常量表达式,其值在积分之后 促销将适合目标类型。
请注意,由于defect report 1449,措辞从原来的C ++ 11标准草案改为我引用的内容。
答案 1 :(得分:3)
这是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
。
答案 2 :(得分:2)
您可以使用:
unsigned char array[] = {
static_cast<unsigned char>(id % 3),
static_cast<unsigned char>(id % 5)
};
答案 3 :(得分:2)
由于id
是unsigned int
,id % 3
的类型也是unsigned int
。
您的编译器有助于警告您unsigned char
(标准为8位),可能太小而无法接收unsigned int
(至少16位)按标准划分的比特)。
当然,在这个特定情况下你会更清楚。使用static_cast<unsigned char>(id % ...)
告诉编译器缩小转换是安全的。