我尝试编译以下代码gcc
并C++11
启用:
unsigned int id = 100;
unsigned char array[] = { id % 3, id % 5 };
我收到这些警告:
在 { } [-Wnarrowing] 内将 '(id % 3u)' 从 'unsigned int' 缩小为 'unsigned char' 的转换
有没有办法帮助编译器找出id % 3的结果适合unsigned char?
在这种特定情况下,制作id
const或constexpr将解决问题:
constexpr unsigned int id = 100;
因为有一个常量表达式的例外情况,其转换后的结果将适合目标类型。
在更一般的情况下,您还可以使用static_cast将结果转换为无符号字符:
{ static_cast<unsigned char>( id % 3), static_cast<unsigned char>( id % 5) }
^^^^^^^^^^^ ^^^^^^^^^^^
我们可以在C++ 标准草案的List-initialization部分中找到常量表达式和缩小转换的例外,它说:8.5.4
缩小转换是隐式转换
并包括以下项目符号(强调我的):
- 从整数类型或无作用域枚举类型到不能表示原始类型的所有值的整数类型,除非源是一个常量表达式,其值在整数提升后将适合目标类型。
请注意,由于缺陷报告 1449,措辞从最初的 C++11 标准草案更改为我在上面引用的内容。
几乎所有数学运算都将其参数转换为 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);
}
a mod b 的结果应该是两种类型中最小的,因为它不能更大。可能应该为签名/未签名做一些工作,但我会先下注。
所以id %mod% 3
最终成为char
.
作为id
一个unsigned int
,类型id % 3
也将是一个unsigned int
。
您的编译器会帮助您警告您unsigned char
(标准为 8 位)可能太小而无法接收unsigned int
(标准至少为 16 位)。
当然,在这种特殊情况下,您会更清楚。用于static_cast<unsigned char>(id % ...)
告诉编译器缩小转换是安全的。
您可以使用:
unsigned char array[] = {
static_cast<unsigned char>(id % 3),
static_cast<unsigned char>(id % 5)
};