我有一个带有一个未定义和两个用户值的枚举:
class enum E
{
UNDEFINED,
VALUE1,
VALUE2
};
我想添加VALUE3
,但我担心其中有很多代码:
assert(val != E::UNDEFINED);
if(val == E::VALUE1)
{
}
else
{
// Without an assert this wrongly assumes E::VALUE2
}
和:
something = (val == E::VALUE1) ? a : b; // last part assumes E::VALUE2
我喜欢编译器警告switch语句不处理所有枚举,并想知道是否有类似的东西可以显示上述所有实例?
我担心我不会找到并更新上述所有实例。
编译器是Clang
答案 0 :(得分:3)
枚举不限于您给其命名的值。来自cppreference(格式是我的):
枚举是一种独特的类型,其值被限制在一个值的范围内(有关详细信息,请参见下文)
其中可能包含几个明确命名的常量(“枚举数”)。常量的值是称为枚举的基础类型的整数类型的值。
“下面的细节”解释了如何确定枚举基础类型。在此范围之外,我们(通常)仅给某些值命名,例如:
enum foo {A,B,C};
int main() {
foo x = static_cast<foo>(42);
}
此代码完全正确。 x
的基础值为42
。该值没有名称,但这并不重要……除非您假设确实如此。
该代码对此做出了错误的假设:
assert(val != E::UNDEFINED);
if(val == E::VALUE1)
{
}
else
{
// Without an assert this wrongly assumes E::VALUE2
}
此代码需要固定(与是否向枚举添加新的命名常量无关)。
现在要进行更严肃的审判以回答问题...
当if-else
链未涵盖所有枚举值时,将无法获得警告。您可以做的是将对枚举的所有if-else
使用都变成错误。考虑一下这里实际发生的情况:
if (x == E::VALUE1) do_something();
switch(x) {
case E::VALUE1 : return 1;
}
在if语句中,我们称为operator==(foo,foo);
,其返回值要么是bool
,要么隐式转换为1。使用开关,则不需要任何操作。我们可以利用它来将if-else
枚举的用法转换为错误。忍受我,我将分两个步骤进行说明。首先让我们为if( x == E::VALUE1)
创建一个编译器错误:
class helper {
operator bool(){ return false;}
};
helper operator==(E,E){
return {};
}
现在if (x == E::VALUE1)
呼叫helper operator==(E,E)
,就可以了。然后将结果转换为bool
,但由于转换为private
而失败。在开关中使用枚举仍然可以,您可以依靠编译器错误/警告。基本思想是使某些东西只有在被调用时(在错误/正确的上下文中)才能编译失败。 (Live Demo)。
缺点是operator==
的所有其他使用也已损坏。我们可以通过修改帮助程序和呼叫站点来解决它们:
#include <type_traits>
enum E {VALUE1};
struct helper {
bool value;
private:
operator bool(){ return false;}
};
helper operator==(E a,E b){
return {
static_cast<std::underlying_type_t<E>>(a) == static_cast<std::underlying_type_t<E>>(b)
};
}
int main() {
E x{VALUE1};
//if ( x== E::VALUE1); // ERROR
bool is_same = (x == E::VALUE1).value;
switch(x) {
case E::VALUE1 : return 1;
}
}
是的,必须编写.value
是一个很大的不便,但是通过这种方式,您可以将if
s中对枚举的所有使用都变成错误,而其他所有内容仍然可以编译。另外请注意,您必须确保涵盖所有您想抓的情况(例如!=
,<
等)。