【问题标题】:Expecting warnings when casting enums to integers with overflow将枚举转换为具有溢出的整数时会出现警告
【发布时间】:2021-08-04 07:56:31
【问题描述】:

我们有一个大型 C++ 项目,我们依靠编译器警告和 Flexelint 来识别潜在的编程错误。我很好奇一旦我们不小心尝试将枚举值转换为更窄的整数,他们会如何警告我们。

正如 Lint 所建议的,我们通常执行从枚举到整数的静态转换。 Lint 不喜欢隐式转换。我们通常会转换为方法所期望的确切类型。

我得到了有趣的结果,请看这个实验:

#include <iostream>
#include <string>
#include <stdint.h>

void foo(uint8_t arg)
{
    std::cout << static_cast<int>(arg) << std::endl;
}

enum Numbers
{
    hundred  =  100,
    thousand = 1000,
};

int main()
{
    std::cout << "start" << std::endl;
    foo(static_cast<uint8_t>(hundred));   // 1) no compiler or lint warning
    foo(static_cast<uint8_t>(thousand));  // 2) no compiler or lint warning
    foo(static_cast<int>(thousand));      // 3) compiler and lint warning
    foo(thousand);                        // 4) compiler and lint warning
    std::cout << "end" << std::endl;
}

http://cpp.sh/5hpyz

第一个案例无关紧要,只提好的案例。

有趣的是,在进行隐式转换时,我只在后两种情况下收到编译器警告。情况 2 中的显式转换将截断值(输出为 232,如下两个),但没有警告。好的,编译器可能假设我知道我在这里做什么,我的显式转换为uint8_t。很公平。

我希望 Lint 能在这里帮助我。我在Gimpel's online Lint 中运行此代码,但也没有收到任何警告。仅在后两种情况下再次出现此警告:

warning 569: loss of information (call) in implicit conversion from 'int' 1000 (10 bits) to 'uint8_t' (aka 'unsigned char') (8 bits)

同样,在案例 2) 中显式转换为 uint8_t,这会截断我的值,根本不会打扰 Lint。

假设枚举中的所有值都适合uint8_t。但在某些未来,我们会添加更大的值(或者说:总共超过 256 个值),将它们强制转换,而不会注意到这会截断它们并得到意想不到的结果。

默认情况下,我总是转换为目标变量大小(案例 2))。鉴于这个实验,我想知道这是否是一种明智的方法。我应该转换为最广泛的类型并改用隐式转换(案例 3)吗?

获得预期警告的正确方法是什么?

【问题讨论】:

  • 强制转换是告诉编译器“我知道我在做什么”的方式,因此它不会发出任何警告

标签: c++ enums casting lint


【解决方案1】:

你也可以写 foo(uint8_t{thousand}); 来代替 static_cast。这样,如果千位对于 uint8_t 来说太大,您将收到编译器错误/警告。但是不知道lint是怎么想的

【讨论】:

    【解决方案2】:

    这是我也遇到的问题。我发现最好的方法是编写一个函数来为你执行强制转换,并且会在基于类型特征出现错误的情况下生成错误。

    #include <type_traits>
    #include <limits>
    
    template<class TYPE>
    TYPE safe_cast(const Numbers& number)
    {
       using FROM_TYPE = std::underlying_type_t<Numbers>;
       // Might have to add some additional code here to fix signed unsigned comparisons. 
       if((abs(std::numeric_limits<TYPE>::min()) > static_cast<FROM_TYPE>(number)) ||
          (std::numeric_limits<TYPE>::max() < static_cast<FROM_TYPE>(number)))
       {
         // Throw an error or assert.
         std::cout << "Error in safe_cast" << std::endl;
       }
          
       return static_cast<TYPE>(number);  
    }
    

    希望对你有帮助

    附言如果你可以用 constexpr 重写它来编译时间,你也可以使用static_assert

    【讨论】:

    • 这样的检查范围很脆弱。例如,如果TYPEFROM_TYPE 的符号不同,那么它就会中断。 Use std::in_range instead
    • @phuclv 没错,我没有时间也包含该代码,但确实包含了评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多