我一直在使用函数输入参数的枚举,但我注意到这可能非常危险。
例如:
enum class MYENUM {
X1 = 0, X2
};
std::map<MYENUM, int> mymap;
//init mymap here with known enum values of X1 and X2
int MyFunc(const MYENUM& input) {
return mymap.at(input);
}
int main() {
MyFunc(static_cast<MYENUM>(10000));
}
因此10000
不是有效的enum
值。你个人将如何找到解决方案?您会将map
访问封装在try
块中,而将catch
封装在exception
中吗?如果我有许多函数使用用户输入的枚举值访问容器信息,那么你会把try catch
也放在所有函数中吗?
您个人将如何找到解决方案?
简单。引入类枚举的原因之一是类型安全-它们不会自动接受任何值。所以,你这里的代码
MyFunc(static_cast<MYENUM>(10000));
只是绕过了语言提供的安全性。解决方案很简单——不要通过强制转换来规避安全性,编译器也不会允许您使用不正确的值。
如果其他人使用演员阵容,这与你无关。有无数种方法可以绕过内置的类型安全,为函数提供完全伪造的值。图书馆作家对此无能为力。
因此10000不是有效的枚举值
这可能是真的,但不是因为它不等于X1
或X2
。枚举没有来容纳其中一个命名枚举器。它可以保存其整数范围内的任何值。
现在,MYENUM
的实际范围取决于编译器,只要它选择的积分底层类型可以表示X1
和X2
。因此,它可以是一个int8_t
,例如,它不能容纳10000。
(实际上,这里可能是int
。(
您个人将如何找到解决方案?您会将映射访问封装在try块中并捕获异常吗?
如果您愿意,可以。
对于这样的函数,有很多错误处理方法,无论它们是否涉及枚举。您可以抛出一个异常(.at()
调用已经为您完成了这项操作,但如果您愿意,您可以捕获它并抛出自己类型的异常(。
您可以填充错误代码,返回占位符值,返回std::optional
…或者忽略它,并记录函数的用户必须传递enum
的命名成员,或者违反某种";未定义行为";(在这种情况下,可能选择让函数像传递X1
一样工作,这样它至少有的事情要做(。
所有常用选项。
如果我有很多函数用用户输入的枚举值访问容器信息,你会在所有函数中都放try-catch吗?
在选择输入参数错误处理方法时,确实需要考虑这一点。
enum
没有本质上的危险,使用enum
参数的函数签名也不是一种糟糕的做法。在您的特定示例中,10000
仍然是有效的enum
,只是没有名称。
这只会在查找std::map<MyEnum, Something>
或使用switch (myEnum)
时成为问题。为了缓解这些问题,您可以对枚举值执行健全性检查:
// specify a type for MyEnum explicitly so that we know we can fit values
// such as 100000
enum class MyEnum : unsigned {
X1 = 0, X2
};
// On a side note, enums are so small in size that it makes no sense to
// pass them by reference, simply pass them by value everywhere.
constexpr bool sanityCheck(MyEnum e) {
return e == MyEnum::X1 || e == MyEnum::X2;
}
int MyFunc(const MyEnum& input) {
assert(sanityCheck(input));
return mymap[input];
}
您甚至可以创建一个assert
函数,该函数只在发布版本上编译,运行时成本为零。这将比到处放置try-catch
更有效。
但最好的解决方案是在使用static_cast
创建MyEnum
时要小心。如果不能100%确定值是否有名称,请不要将结果传递给接受MyEnum
的函数。