是否可能为三元运算符设置空白的第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;
}
是否有任何解决方案不涉及已经存在的变量,函数或对象的重述?例如,考虑两个函数foo
和goo
,其中foo
返回bool
。假设由于某种原因,不能再次调用函数,例如在后续调用中更改状态。
int main()
{
( foo() ) ? goo() : ; // won't compile
return 0;
}
答案 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语句。这似乎更清楚。