我的研究尚未得出答案(在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的范围会发生什么?这里有“最佳实践”吗?
答案 0 :(得分:5)
但它做对了吗?
假设该值对枚举类型有效,是。
如果返回的值超出myNum_t的范围会怎样?
取决于。
最多(并且不包括)最近的2的幂,你可以保证得到那个值。您的代码然后用它做什么取决于您。换句话说,给定您的代码,(int)(myNumType_t)7
保证等于7.这很重要,因为有些人对枚举值使用按位运算,因此如果枚举有BIT0 = 1, BIT1 = 2, BIT2 = 4
,则意图{ {1}}可以存储在相同的枚举类型中。
对于较大的值,可以说没有多少用处。有些实现会将您的枚举存储在单个(8位)字节中,因此在这些实现中,BIT0 | BIT1 | BIT2
不可能等于257.在其他实现中,它可能是。最好通过事先检查整数值来避免这种情况。
答案 1 :(得分: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";
即使您的测试用例发送的邮件包含无效值,您最终也会对所有值有效的原因感到惊讶!
答案 2 :(得分: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...
}
}
答案 3 :(得分:0)
是的,从int类型转换为枚举类型是安全的。
§4.5整体促销
其基础类型为固定(7.2)的无范围枚举类型的prvalue可以转换为其基础类型的prvalue。此外,如果可以对其基础类型应用整数提升,则固定基础类型的无范围枚举类型的prvalue也可以转换为提升的基础类型的prvalue。
只需返回枚举类型,这样就可以避免任何演员阵容:
myNum_t getMessageNumType();
答案 4 :(得分:0)
您可以使用强制转换将整数值转换为枚举值。如果您这样做,您将负责确保结果值有意义。在示例代码中,您应确保getMessageNumType()
返回合适的值。
存储在枚举类型中的值不限于枚举器指定的值。例如,使用枚举器和适当的重载运算符来实现位掩码是很常见的:
enum flag_values {
flag1 = 0x01,
flag2 = 0x02,
flag3 = 0x04,
flag4 = 0x08
};
flag_values flags = static_cast<flag_values>(flag1 | flag2);
|
运算符应该完全由重载运算符完成;为简单起见,我把它留了下来。演员阵容还可以,但到处写作都很乏味。