使用逻辑 ||带有枚举值

Posted

技术标签:

【中文标题】使用逻辑 ||带有枚举值【英文标题】:Using logical || with enum values 【发布时间】:2012-10-04 14:24:15 【问题描述】:

我有一个 MPMoviePlayerController 实例。我希望检查它的playbackState 属性以获取多个值之一。因此,我会这样做:

if (moviePlayer.playbackState == (MPMoviePlaybackStateStopped ||
                                  MPMoviePlaybackStatePlaying ||
                                  MPMoviePlaybackStatePaused)) 
    // ...
    // Perform some logic
    // ...

这按预期工作,但会导致编译器警告:

使用逻辑“||”使用常量操作数。

编译器的解决方法是改用按位| 运算符。在 Stack Overflow 上搜索你会发现 couple 的 answers 暗示了同样的事情。 但是使用按位 OR 真的不是我需要的。

MPMoviePlaybackState 在 MPMoviePlayerController.h 中声明:

enum 
    MPMoviePlaybackStateStopped,
    MPMoviePlaybackStatePlaying,
    MPMoviePlaybackStatePaused,
    MPMoviePlaybackStateInterrupted,
    MPMoviePlaybackStateSeekingForward,
    MPMoviePlaybackStateSeekingBackward
;
typedef NSInteger MPMoviePlaybackState;

这不是位掩码(而且它也没有多大意义——枚举值是互斥模式,而不是要组合的标志)。我真的很想使用符合逻辑的||

(在我的特定情况下,基础值为 0,1,2,按位示例可能有效,但这只是巧合。)

我应该如何改写以避免警告或者我可以使用什么 #pragma clang diagnostic ignored ... 来消除警告?

(指向所有此类诊断列表的奖励积分 - 我似乎无法在手册中找到一个。)

提前致谢!

【问题讨论】:

【参考方案1】:

显然,(enumval1 || enumval2 || ..) 是错误的。您不能像这样使用|| 运算符,而只能使用逻辑表达式。| 运算符有效,因为它是一个简单的按位 OR,只有当您的枚举成员是 2 的不同幂(例如 1、2、4、8,...)时,它才会为您工作。

它与二进制数字的按位表示有关,如果数字是2的幂,则如下所示:2->10、4->100、8->1000等。所以,对于2 | 8就像0010 | 1000 = 1010,不是零,if 语句将继续。

编译器警告是完全正确的并且在这一点上有所帮助。使用switch(..)if(..) else if(..) 语句,或使您的枚举如下:

enum yourEnum

  enumval1 = 1 << 0;
  enumval2 = 1 << 1;
  enumval3 = 1 << 2;
  // ...

【讨论】:

我不知道这里的“明显”、“错误”或“不能”。语义上 (X||Y||Z) 正是我所追求的。这是正确的。我正在使用它。谢谢你的解释,但我确实理解按位表示。有问题的枚举是 Apple 的,所以很遗憾我无法更改它。 这与苹果无关。当您使用 || 运算符时,操作数将转换为布尔类型。它变成真/假逻辑运算,而不是按位。 啊,是的——是选角。谢谢!你是对的,它与按位表示无关。 @wilsteel 的回答是最好的。 仍然不明白为什么他的答案是最好的。 :) 您对c++中逻辑运算符的基本点有一些误解,为什么提供一个使用switch语句的例子是最好的答案?.. "Best":只是它捕获了我的原始(错误)代码的意图——我想在枚举中交替可能的模式。鉴于底层表示,我不能用|| 做到这一点——愚蠢的错误; IMO switch 是最清晰的选择——if 带有很多子句的语句(通常)更难阅读。【参考方案2】:

你为什么不这样做呢?

if ((moviePlayer.playbackState == MPMoviePlaybackStateStopped) ||
    (moviePlayer.playbackState == MPMoviePlaybackStatePlaying) ||
    (moviePlayer.playbackState == MPMoviePlaybackStatePaused)) 
    // ...
    // Perform some logic
    // ...

【讨论】:

实际上,-O3 的代码可能是相同的...并且它将使用 x86 的 bsrq 指令来测试集合成员资格,而不是执行简单的 cmp/je , 在这两种情况下。就个人而言,我会使用if 处理一两个案例,switch 处理五个或更多案例,而 OP 的示例位于中间的灰色区域。【参考方案3】:

我建议使用带有跌谷逻辑的 switch/case 块:

switch(moviePlayer.playbackState)
    case MPMoviePlaybackStateStopped: /* falls through */
    case MPMoviePlaybackStatePlaying: /* falls through */
    case MPMoviePlaybackStatePaused:  /* falls through */
        // your stuff

这将以尽可能少的代码实现预期的行为。枚举是为确切的开关案例类型的业务而制作的。并且它们比“if”语句更优化了性能,因为 CPU 在到达代码时甚至不必测试这些值。编译器在该位置计算正确的 ASM 跳转偏移量。所以它和闪电一样快:)

【讨论】:

投了反对票,因为您实际上没有解释 OP 的代码出了什么问题(|| 的错位),以及关于性能的红鲱鱼(无论如何都是不正确的)。而对于“u”。 关于性能,我是对的。只是自己测试了一下以确保。这是代码:pastebin.com/7GNkpny5 关于||:你没抓住重点。这不仅仅是“不明智”; OP 对|| 的使用完全不正确。 呵呵,“那是因为你在使用优化。”欢迎来到二十世纪下半叶。 en.wikipedia.org/wiki/Switch_statement 对我来说看起来不错,尽管“历史”部分的符号有点重。常见的优化在标题为“优化开关”的部分中讨论。

以上是关于使用逻辑 ||带有枚举值的主要内容,如果未能解决你的问题,请参考以下文章

带有浮点原始值的 Swift 枚举的“枚举大小写的原始值不是唯一的”

带有枚举值的 jpql IN 查询

使用带有标志枚举的 ProtoBuf-Net 时出错

springbootvalidator枚举值校验

枚举VS静态类(正常和带字符串值)

WPF DataGrid 带有一键组合框,显示按枚举名称排序的枚举值