为什么直接比较两个枚举类型会报错?

15

我正在将一些代码移植到新平台, 结果出现了一个错误,指出比较了两个来自不同枚举列表的枚举器。我不明白为什么会出现这种错误。

C规范(6.7.2.2节)中的"enumeration specificers"部分规定如下:

枚举器列表中的标识符被声明为具有 int 类型的常量,并可以出现在任何允许这样的位置。127) 具有 = 的枚举器将其枚举常数定义为常量表达式的值。 如果第一个枚举器没有 =,则其枚举常数的值为0。

所以我应该能够像使用 int 常量一样使用枚举成员。 在这个小的示例程序中:

enum first {
  a,
  b
};

enum second {
 c,
 d
};

int main(){
    enum first myf = a;
    enum second mys = c;

    if(myf == mys)
        printf("same value\n"); 
    return 0;
}

使用gcc -Wall -Werror编译时,我会收到以下错误信息:

 

error: comparison between ‘enum first’ and ‘enum second’ [-Werror=enum-compare]

我知道如果将myfmys都强制转换为int,编译器就会满意,就像我可以使用myfmys的值来设置一对int,然后进行比较;但是,为什么我必须这样做才能消除警告?为什么在第一次出现这个警告呢?我肯定没有看到其中存在的风险。


注意:
我已经阅读了关于此枚举-比较标志的gcc文档,但它并没有说太多有用的信息:

 

-Wenum-compare
  警告在不同枚举类型之间的值比较。在C++中,条件表达式中的枚举不匹配也会被诊断,并默认启用该警告。在C中,该警告由-Wall启用。


2
gcc文档说明得很清楚了。有什么不清楚的吗? - SomeWittyUsername
2
@icepack - 这怎么解释呢?它只是说明了它的功能,但并没有解释为什么有人需要它。如果两个不同枚举类型的成员都是int类型,那么为什么在比较一个int和另一个int时需要警告呢? - Mike
1
我认为它只是在警告你,因为这很容易出错。我能想到的另一件事是枚举可能会被不同类型表示。 - teppic
1
@Mike 关键词是“枚举不匹配” - 这通常表示语义错误。请参见下面的答案以获取详细说明。 - SomeWittyUsername
2
@Mike,如果你定义了不同的“枚举”,那么这很可能是因为它们是用于标记_不同_的事物,例如enum color {blue, yellow, green};enum form {round, square, pentagonal};。那么问你的“颜色”是否是“五边形”的说法就没有任何意义了... - vonbrand
当你启用了-Werror选项后,你不能继续抱怨出现错误。 - v.oddou
4个回答

18

这并不是由于标准遵从性问题而发出的警告,而是属于那种“这看起来不太对”的警告。如果你考虑到枚举类型的典型用法,这样的比较在许多情况下都没有意义。例如:

enum Day {
  Sunday,
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday
};

enum Month {
  January,
  February,
  March,
  April,
  May,
  June,
  July,
  August,
  September,
  October,
  November,
  December
};

enum Day day = Wednesday;
enum Month month = April; 

if (day == month) { ... }

这个表达式的值为真,但一般来说该比较并没有太多意义。如果您确认您想要这么做,类型转换将会让编译器信服,就像您已经指出的那样。


是的,一般来说这很有道理...当然,在我的实际用例中,相比于天数转换成月份,它更有意义,但是gcc无法知道这一点。;) 我不确定你是否知道在打开-Wall时关闭-Wenum-compare的方法? - Mike
1
@Mike 尝试将 -Wall -Wno-enum-compare 结合使用。这样可以获得除枚举比较警告之外的“所有”警告。 - FatalError
7
谁还把星期日当作一周的第一天啊?(附赞一个,顺便说一句) - Oliver Charlesworth
1
@OliCharlesworth,是的,ISO标准规定星期一为一周的第一天,但是微软实际上使用星期日:http://msdn.microsoft.com/en-us/library/system.dayofweek(v=vs.110).aspx - Sebastian

3

if((int)myf == (int)mys)

这样就可以了。但这是一种不好的做法,只有在两个枚举类型是同一组的不同“版本”时才使用,比如新版本会在末尾包含新的关键字。


1
现在,人们会使用if( static_cast<int>(myf) == static_cast<int>(mys) )而不是隐式转换(参见这个stackoverflow答案)。 - Martin
@Martin 你是在指C ++吗? - syockit

2
它正在警告您,因为您已经使用了警告标志。标志说明没有解释其存在的原因,但可以安全地假设它存在是为了防止不同枚举类型之间的意外比较,因为这通常会导致错误。此外,您正确地指出,您可以像使用int常量一样使用枚举值。如果您写成if (myf == c),那么它很可能不会触发警告(我说“很可能”是因为我没有进行实验,而GCC可以自己决定是否给出警告,但从技术上讲,c只是一个整数常量,不带有enum second类型)。但是,您实际上是在显式比较两个不同枚举类型的值。

1
如果你说“if (myf == c)”,那么它很可能不会产生警告(一个有趣的理论)。我尝试了一下,但仍然产生了警告(供你参考)。好吧,既然我需要-Wall -Werror,我就必须做一个本质上不需要的类型转换。 - Mike
1
@Mike - 只需使用“-Wno-enum-compare”进行编译? - teppic
@Mike:好的,所以GCC认为这是一个可以警告的情况。这并不令人惊讶,因为这个警告只是为了标记一些在技术上并不违法的东西。 - Lily Ballard

2

我在发布问题时看到了那个链接,但由于语言不同,问题在技术上是不同的,答案在这里也没有意义;因此我忽略了它。然而,我应该在问题中提到它。 - Mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接