空白或不执行任何操作的三元运算符的第二个或第三个参数
Blank or do-nothing 2nd or 3rd argument for ternary operator
是否可以为三元运算符设置一个空白的第二个或第三个参数,或者有一个非上下文特定的参数,表示"什么都不做"?在下面的示例中,我希望一个三元运算符将整数变量乘以 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;
}
如果要使用三元运算符,则可能需要分配其结果:
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,否则我甚至不确定是否获得同样好的结果 - 而且它们更好的机会充其量对我来说非常渺茫。
(x % 2 == 0) ? x *= 2 : x = x;
条件运算符不是 if 语句,它旨在生成一个值,如果您不需要该值,就像上面的情况一样,只需键入if
,因为它是:
if (x%2==0) x*=2;
这更易于阅读和维护,并且在所有其他方面都等效。如果您认为您的代码在任何方面都会更好,请重新考虑它。
从问题的评论来看,您似乎想了解它是如何工作的,而不仅仅是编写看起来很奇怪的代码。如果是这种情况,运算符非常有趣,但有趣的部分不是您是否可以将其与 3 个或 2 个运算符一起使用。相反,运算符最有趣的行为是当第二个和第三个参数不属于同一类型时它产生什么(同样,它被设计为产生一个值)。
与所有其他生成值的表达式一样,无论条件的值是多少,三元运算符的类型都是固定的,因此编译器必须确定整个表达式的类型是什么,并且它必须是与两个参数兼容的类型。这些规则相当有趣,太复杂了,无法在这里解释,但你应该看看有条件的爱:FOREACH Redux。
另外,虽然它不符合你的问题,这就是我之前没有提出它的原因,但不是两个分支都需要产生一个值,它们中的一个或两个都可以是throw
表达式,在这种情况下,类型是另一个参数的类型。此外,如果两者都是throw
表达式或 void
类型的表达式,则三元运算符的类型void
本身。
办法跳过三元运算符的参数。 如果函数需要调用一次,那么你可以这样使用。 如果 foo() 和 goo() 的返回值都是布尔值,则可以使用 simple or 运算符来获得最终结果。
bool ResultOfFoo = foo();
bool ResultOfGoo = goo();
bool RequiredResult = ResultOfFoo ? ResultOfGoo : ResultOfFoo ;
优化
bool RequiredResult = ResultOfFoo || ResultOfGoo;
目前,(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 语句。似乎更清楚了。
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 在 2D 向量中使用第三个 [ ] 有什么意义?
- 如何通过按下第三个窗口中的按钮,将QString从一个窗口获取到另一个窗口
- 如何知道n!是否可以表示为三个连续数字的乘法?
- 我有三个 getline,但是一旦编译,输入就太多了
- 我遇到了黑客排名中的问题"TWO STRINGS"的三个测试用例的分段错误。原因是什么?
- 有没有更好的方法对C++中的三个整数进行排序?
- 如何检查第三个 API 是否在 Linux 中为 c/c++ 程序创建了一个新线程?
- 首先处理第二个和第三个堆与第一个和第二个堆之间的逻辑差异
- 对条件表达式结果的赋值(其中第二个和第三个操作数是相同类型和值类别的变量)是否仍然存在?
- 运行时间错误:程序跳过提示,以获取第二名和第三个名称
- 如何按第一个,然后是第二个,然后是第三个对 2d 数组进行排序,..使用 C++ 的列
- 警告:条件的第二个/第三个操作数不起作用 [-Wunused-value]
- 我们如何确保传递的 2 个参数被视为第一个和第三个,而第二个 AD 第四个被视为默认值
- 空白或不执行任何操作的三元运算符的第二个或第三个参数
- 为什么在条件运算符(?:)中,第二个和第三个操作数必须具有相同的类型
- 第二个参数和第三个参数在fwrite()中的作用是什么?为什么我们在这里需要 3rcount?
- 如果第二个或第三个参数抛出表达式,则失去左值
- 我可以在三元运算符的第二个或第三个操作数中使用多个指令吗?
- 什么可能导致第一个代码片段的输出不同于第二个和第三个