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

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

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

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

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

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

MPMoviePlaybackState 在 MPMoviePlayerController.h 中声明:

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

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

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

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

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

提前致谢!

【问题讨论】:

    标签: objective-c mpmovieplayercontroller clang


    【解决方案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 对我来说看起来不错,尽管“历史”部分的符号有点重。常见的优化在标题为“优化开关”的部分中讨论。
    猜你喜欢
    • 1970-01-01
    • 2017-05-05
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 2014-06-28
    • 1970-01-01
    • 2010-09-12
    • 2013-12-21
    相关资源
    最近更新 更多