三元运算符的空白或无任何第二或第三个参数

时间:2013-09-07 01:53:10

标签: c++ c++11 ternary-operator conditional-operator

是否可能为三元运算符设置空白的第2或第3个参数,或者是否具有非特定于上下文的参数并且意味着“什么都不做”?在下面的例子中,我想要一个三元运算符,如果它是偶数,则将整数变量乘以2,否则不执行任何操作。除了自我赋值,增加或减去零,或乘以或除以1之外,我无法想到第三个参数。无论如何,它们都是特定于上下文的。我想要一些对所有三元运营商都“无所事事”的东西。我试着将参数留空,但不会编译。

#include <iostream>

int main()
{
    int x = 5;
    (x % 2 == 0) ? x *= 2 : x = x; // or x += 0, x -= 0, x *= 1, or x /= 1
    std::cout << x << std::endl;
    return 0;
}

是否有任何解决方案不涉及已经存在的变量,函数或对象的重述?例如,考虑两个函数foogoo,其中foo返回bool。假设由于某种原因,不能再次调用函数,例如在后续调用中更改状态。

int main()
{
    ( foo() ) ? goo() : ; // won't compile
    return 0;
}

4 个答案:

答案 0 :(得分:7)

如果您打算使用三元运算符,您可能想要分配其结果:

x = (x % 2 == 0) ? 2 * x : x;

就个人而言,我认为我只是使用了一点点数学:

x <<= ((x & 1) ^ 1);

x&1给出x的最低有效位,如果x为奇数,则为1,如果为偶数则为0。 ^ 1部分会翻转该位,因此如果x为偶数则为1,如果x为奇数则为0。然后我们将x向左移动那么多位。移位0位显然使x保持不变。向左移一位将x乘以2.

至于为什么后者会(或者至少可能)更为可取,它主要归结为一个问题,即你是否真的关心这种情况下的表现。如果您遇到性能无关紧要的情况,那么if ((x%2)==0) x *= 2;之类的东西可能是您的最佳选择。

我至少猜到了问题的至少部分原因是对效率的关注。如果是这样,纯粹的数学方法可能是更好的选择。例如,让我们考虑VC ++为这两个代码生成的代码:

; mathematical version:
mov eax, DWORD PTR _x$[esp-4]
mov ecx, eax
not ecx
and ecx, 1
shl eax, cl

[注意:对于这个源代码,g ++生成几乎相同的目标代码]。

忽略从存储器加载x的(可能)时间,这应该在几乎任何英特尔处理器上执行不超过4个时钟周期,回到386左右。更好的是,我希望只是关于任何处理器的任何编译器产生非常相似的结果 - 对于几乎任何合理的处理器,从源代码到汇编语言的直接,字面翻译将在四个指令中进行实际数学运算,每个指令都是简单快速的尽可能。

使用if语句的版本如下:

; Line 2
    mov ecx, DWORD PTR _x$[esp-4]
    mov eax, ecx
    and eax, -2147483647            ; 80000001H
    jns SHORT $LN5@f
    dec eax
    or  eax, -2                 ; fffffffeH
    inc eax
$LN5@f:
    lea eax, DWORD PTR [ecx+ecx]
    je  SHORT $LN1@f
; Line 3
    mov eax, ecx
$LN1@f:

编译时,这不是太糟糕。它至少避免div这将是实现%2的明显方式。不幸的是,它仍然不够聪明,没有竞争力 - 它仍然有几个分支,其中一个可能不会很容易预测,所以大约一半的时间我们将支付错误预测分支的价格。

根据编译器的不同,您可以(并且将会)看到比这更好的内容。例如,使用g ++,我得到了这个:

    mov eax, DWORD PTR [ebp+8]
    and eax, 1
    test    eax, eax
    jne L2
    sal DWORD PTR [ebp+8]
L2:
    mov eax, DWORD PTR [ebp+8]

虽然肯定更好而不是VC ++为此代码所做的事情,但它仍然远不及数学版本那么好。特别是,除非最低有效位是相当可预测的偶数或奇数,否则该分支很可能在大约一半时间内被预测。

底线:最好的情况是,可能接近匹配数学版本 - 但这将取决于编译器和输入数据合作。除了编译器和输入数据最偶然的组合之外,它几乎肯定会慢到至少2倍,而10倍甚至不会令人感到意外。

当然,根据我使用的标志,编译器版本等,我可能能够从任何编译器中获得比实际更好的结果。通过一些持久性,我可能甚至得到的结果等于代码的数学版本。除非我非常了解目标编译器和CPU,否则我会非常不确定即使获得了相同的结果 - 并且他们更有可能让我更好地打击我。

答案 1 :(得分:2)

(x % 2 == 0) ? x *= 2 : x = x;

条件运算符是一个if语句,它意味着产生一个值,如果你不需要这个值,就像上面那样,只是键入if,因为它应该是:

if (x%2==0) x*=2;

更容易阅读和维护,并且在所有其他方面都相同。如果您认为您的代码会以任何方式变得更好,请重新考虑它。

从问题中的评论看来,您似乎想要了解它是如何工作的,而不仅仅是编写奇怪的代码。如果是这种情况,操作员非常有趣,但有趣的不是你是否可以将它与3或2个运算符一起使用。相反,当第二个和第三个参数的类型不同时,运算符最有趣的行为是产生(同样,它被设计为产生一个值)。

与所有其他值生成表达式一样,无论条件的值是什么,三元运算符的类型都是固定的,因此编译器必须确定整个表达式的类型是什么,并且它必须是兼容的类型两个论点。这些规则相当有趣且太复杂,无法在此解释,但您应该查看 Conditional Love: FOREACH Redux

此外,虽然它不适合您的问题,这就是为什么我之前没有提到它的原因,但是两个分支都不需要来产生一个值,其中一个或两个都可以是throw表达式,在这种情况下,类型是另一个参数的类型。此外,如果两者都是throw表达式或类型void的表达式,则三元运算符的类型本身为void

答案 2 :(得分:1)

您无法跳过三元运算符的参数。    如果需要调用一次函数,那么你可以像这样使用。    如果foo()和goo()的返回值都是boolean,你可以使用simple或operator来获得最终结果。

bool ResultOfFoo =  foo();
bool ResultOfGoo = goo();

bool RequiredResult = ResultOfFoo ? ResultOfGoo : ResultOfFoo ;

<强>优化

bool RequiredResult = ResultOfFoo || ResultOfGoo;

答案 3 :(得分:0)

目前,(x % 2 == 0)? x *= 2 : x = x并不比if语句好:

if (x % 2 == 0) x *= 2; // This is okay for one-liners.

如果要保留三元运算符,则不需要编写x = x,可以将整行作为赋值语句并使用三元运算符来选择值:

x = (x % 2 == 0)? x * 2 : x;

我只是坚持使用if语句。这似乎更清楚。