9

我的研究还没有给出答案(在 SOF 中),但我确信之前一定有人问过这个问题,如果是这样,我很抱歉。

我在 C++ 中创建了一个枚举类型,然后我从消息头中读取了一个值,并希望将其存储到我的枚举类型的变量中,例如:

// My defined enum type
enum myNumType_t
{
    MY_NUM_UNKNOWN = 0,
    MY_NUM1 = 1,
    MY_NUM2 = 2,
    MY_NUM3 = 3,
    MY_NUM4 = 4,
    MY_NUM5 = 5,
};

// In the code
int main()
{
    myNum_t myNum = MY_NUM_UNKNOWN;
    myNum = getMessageNumType(); // returns an int

    return 0;
}

所以,这段代码不能在 c++ 中编译,因为它不能将 int 转换为 myNum_t,这很公平。因此,如果我将其myNum = (myNum_t) getMessageNumType();转换为现在当然可以编译。但它做正确的事吗?如果返回值超出 myNum_t 的范围会怎样?我在这里缺少“最佳实践”吗?

4

5 回答 5

5

但它做正确的事吗?

假设该值对枚举类型有效,是的。

如果返回值超出 myNum_t 的范围会怎样?

这取决于。

最多(并排除)最接近的 2 次幂,您可以保证获得该值。您的代码如何处理它取决于您。换句话说,给定您的代码,(int)(myNumType_t)7保证等于 7。这很重要,因为有些人对枚举值使用按位运算,因此如果枚举具有BIT0 = 1, BIT1 = 2, BIT2 = 4,则BIT0 | BIT1 | BIT2可以将其存储在相同的枚举类型中。

对于较大的值,可以说没有多大用处。某些实现会将您的枚举存储在单个(8 位)字节中,因此在这些实现中,(int)(myNumType_t)257不可能等于 257。在其他实现中,它可能是。你最好通过事先检查整数值来避免这种情况。

于 2013-10-03T12:58:51.060 回答
3

第一个问题是您是否可以信任值的来源,一般来说,如果您从网络或任何其他输入/输出设备读取消息,我会怀疑这种信任。

如果您不能信任它,您应该能够编写一个小函数来测试接收到的值并将其映射到适当的枚举器,或者如果值超出范围则失败。请注意,在您提供的示例代码中,编译器可以为能够表示 0-7 范围内的所有值的枚举选择任何类型,标准不做任何额外的保证。

虽然实际上编译器很可能会int为这种特殊情况选择更大的类型,但编译器可以假设该值不在该范围之外。如果从消息中解码的值大于 8,您将遇到未定义的行为,并且您可能会得到您并不真正期望的结果。

举个实际的例子,如果枚举数的范围是 0 到 3,两者都包括在内,并且您有以下代码:

enum_t value = (enum_t) getInt();
switch (value) {
case V0:
case V1:
case V2:
case V3:  std::cout << "valid\n"; break;
default:  std::cout << "unknown value\n"; break;
}

允许编译器删除default大小写,因为可以在 中表示的所有值都在enum_t中显式枚举switch,因此在default:格式良好的程序中无法命中大小写。优化器可以将该测试更改为:

enum_t value = (enum_t) getInt();
std::cout << "valid\n";

即使您的测试用例发送带有无效值的消息,您最终可能会惊讶于为什么所有值都是有效的!

于 2013-10-03T12:58:42.950 回答
1

唯一真正安全的 int-to-enum 转换是覆盖枚举中的所有情况,并在错误检查输入可能返回并包括边界枚举时,如果有可能超出范围或返回无效值,然后处理优雅的坏价值。

如果保证枚举是一个连续的范围,那么它会更简单一些:

enum myNumType_t
{
    MY_NUM_UNKNOWN = 0,
    MY_NUM_MIN = 1,
    MY_NUM1 = 1,
    MY_NUM2 = 2,
    MY_NUM3 = 3,
    MY_NUM4 = 4,
    MY_NUM5 = 5,
    MY_NUM_MAX = MY_NUM5,
};

int main() {

    //...getMessage...

    myNumType_t result = static_cast<myNumType_t>(getMessageNumType());

    if(result < MY_NUM_MIN || result > MY_NUM_MAX) {
        //..handle out of range...
    }
}
于 2013-10-03T12:59:58.207 回答
0

您可以使用强制转换将整数值转换为枚举。如果你这样做,你有责任确保结果值是有意义的。在示例代码中,您应该确保getMessageNumType()返回合适的值。

存储在枚举类型中的值不限于由枚举器命名的值。例如,使用枚举器以及适当的重载运算符来实现位掩码是很常见的:

enum flag_values {
    flag1 = 0x01,
    flag2 = 0x02,
    flag3 = 0x04,
    flag4 = 0x08
};

flag_values flags = static_cast<flag_values>(flag1 | flag2);

那个|操作符真的应该用一个重载的操作符来完成;为了简单起见,我把它省略了。演员阵容还可以,但到处写都很乏味。

于 2013-10-03T12:58:10.153 回答
-1

是的,从 int 类型转换为枚举类型是安全的。

§ 4.5 整体促销

其基础类型固定 (7.2) 的无作用域枚举类型的纯右值可以转换为其基础类型的纯右值。此外,如果可以将整型提升应用于其基础类型,则其基础类型固定的无范围枚举类型的纯右值也可以转换为提升的基础类型的纯右值。

只需返回枚举类型,这样您就可以避免任何转换问题:

myNum_t getMessageNumType();
于 2013-10-03T12:39:45.897 回答