使用static_cast创建的枚举调用C++函数的安全性



我一直在使用函数输入参数的枚举,但我注意到这可能非常危险。

例如:

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不是有效的枚举值

这可能是真的,但不是因为它不等于X1X2。枚举没有来容纳其中一个命名枚举器。它可以保存其整数范围内的任何值。

现在,MYENUM的实际范围取决于编译器,只要它选择的积分底层类型可以表示X1X2。因此,它可以是一个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的函数。