【问题标题】:C++11: "narrowing conversion inside { }" with modulusC++11:使用模数“缩小 {} 内的转换”
【发布时间】:2014-11-17 14:21:26
【问题描述】:

我尝试在启用gccC++11 的情况下编译以下代码:

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

我收到以下警告:

在 { } [-Wnarrowing] 中将“(id % 3u)”从“unsigned int”缩小到“unsigned char”的范围内

see demo online

有没有办法帮助编译器找出 id % 3 的结果适合 unsigned char

【问题讨论】:

标签: c++ c++11 narrowing


【解决方案1】:

在这种特定情况下,id constconstexpr 将解决问题:

constexpr unsigned int id = 100;

因为有一个常量表达式的情况例外,其转换后的结果将适合目标类型。

在更一般的情况下,您还可以使用static_cast 将结果转换为无符号字符:

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

我们可以在 draft C++ standard 部分 8.5.4 List-initialization 中找到 常量表达式 和缩小转换的例外情况,其中说:

窄化转换是隐式转换

并包括以下项目符号(强调我的):

  • 从整数类型或无作用域枚举类型到不能表示原类型所有值的整数类型,除了 其中源是一个常量表达式,其值在积分后 促销活动将适合目标类型

注意,由于defect report 1449,措辞从最初的 C++11 标准草案更改为我在上面引用的内容。

【讨论】:

    【解决方案2】:

    几乎所有数学运算都将其参数转换为int,这是 C++ 的一个怪癖。

    这是一个非扩展%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

    【讨论】:

    • 我猜有一个小错误:operator%( LHS&amp;&amp; lhs, mod ) 必须是operator%( LHS&amp;&amp; lhs, mod_t )。除此之外:这仅适用于我转换为 char:id %mod% (char) 3,否则编译器也将 3 解释为 int。有什么办法吗?
    • @m.s.呸,我认为常数是char 而不是int。尽管很可怕,'\3' 是字符常量3。 :/ 可能你最好的选择是 constexpr 上的 id 真的,如果它真的是一个常数。
    【解决方案3】:

    你可以使用:

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

    【讨论】:

      【解决方案4】:

      由于idunsigned intid % 3 的类型也将是unsigned int

      您的编译器会警告您 unsigned char(标准为 8 位),可能太小而无法接收 unsigned int(标准至少为 16 位) )。

      当然,在这种特殊情况下您会更清楚。使用static_cast&lt;unsigned char&gt;(id % ...) 告诉编译器缩小转换是安全的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多