布尔表达式是否像使用 if 或 switch 进行分支一样繁琐?
Is a boolean expression as onerous as branching with if or switch?
我经常将一些if
语句转换为布尔表达式以实现代码紧凑性。例如,如果我有类似的东西
foo(int x)
{
if (x > 5) return 100 + 5;
return 100;
}
我会这样做
foo(int x)
{
return 100 + (x > 5) * 5;
}
这很简单,所以没问题,问题是当我有多个测试时,我可以大大简化它们(以牺牲可读性为代价,但这是一个不同的问题)。
所以问题是,这种(x > 5)
评估是否像明确分支一样繁琐。
在这两种情况下,如果表达式的计算结果为true
,则必须检查表达式(x > 5)
。如前所述,即使未启用任何优化,两个版本也可以编译为同一程序集。
但是,C++核心指南的哲学部分有以下两条规则,您最好注意:
- P.1:直接在代码中表达想法
- P.3:表达意图
尽管无论如何都无法强制执行这些规则,但遵守它们将使您采用带有if
语句的版本。
这样做将使那些必须在您之后甚至几个月后自己维护代码的人不那么繁重。
您似乎将C++语言结构与程序集中的模式混为一谈。考虑到八十年代末或九十年代初的编译器,在这个级别上对代码进行推理可能是可行的。然而,在这一点上,编译器应用了许多优化和转换,其正确性或实用性对普通程序员来说甚至不明显。一个非常简单的例子是初学者假设以下等价的常见错误:
std::uint16_t a = ...;
a *= 2; // a multiplication in assembly
a *= 17; // ditto
a /= 3; // a division in assembly
然后,他们可能会惊讶地发现他们选择的编译器将这些转换为等效的汇编,例如:
a <<= 1u;
a = (a << 4u) + a; // or even (a << 4u) | a if a < 16
a *= 43691u;
请注意,仅当已知a
是除数的倍数时,才允许进行最后一次变换,因此您可能不会经常看到这种优化。它是如何工作的?在数学术语中,uint16_t
可以被认为是残余类环Z/(2^16)Z,在这个环中,对于任何与2^16互质的元素(即不能被2整除)都存在乘法逆。如果d
(例如 3)是 2 的互质,则它具有这样的逆数,如果已知余数为零,则除以d
简直等于乘以d
的倒数。(我不会在这里讨论如何计算这种逆数。
这是另一个令人惊讶的优化:
long arithsum(long n)
{
long result = 0;
for (long i=0; i<=n; ++i)
result += i;
return result;
}
GCC 与-O3
相当平凡地将其转化为一个展开的添加循环。但是,如果你这样做,我的 Clang 版本(9.0.0svn-something)会在你身上拉一个高斯,并将其转换为类似的东西:
long arithsum(long n)
{
return (n * (n+1)) >> 1;
}
无论如何,同样的警告适用于if
/switch
等——虽然这些是控制流结构,所以你会认为它们对应于分支,但事实可能并非如此。同样,如果编译器具有优化规则,这似乎是有益的,或者即使它只是无法使用分支(在给定的体系结构上)无法将自己的 AST 或中间表示转换为机器代码,则看似非分支操作的内容可能会转换为分支操作。
TL;DR:在你试图超越你的编译器之前,首先要弄清楚编译器为简单/可读的代码生成的程序集。如果这个程序集很好,那么使代码更微妙/可读性更差是没有意义的。
假设你的意思是 1/0。当然,由于隐式类型转换,它可能在 C/C++ 中工作,但可能不适用于其他语言。如果这是你想要实现的,为什么不使用三元运算符(? :
),这也使代码更具可读性
foo(int x) {
return (x > 5) ? (100 + 5) : 100;
}
另请阅读这篇 stackoverflow 文章 -- bool 到 int 的转换
- IPC使用多个管道和分支进程来运行Python程序
- 如何删除peer if else分支中的冗长句子
- 如何确保在使用基于布尔值的两个方法之一调用方法时避免分支预测错误
- 如何正确地将分支添加到已存在的树中
- 如何将不同的可执行文件合并到一个窗口框架中进行编码?像浏览器一样
- 如何将分支添加到已存在的TTree:ROOT
- 为什么在C++中对链表这样做?(像堆叠一样处理它们)
- 堆栈和队列是否像C++中的数组一样传递?
- 如何删除 LLVM 中的不规则分支?
- 是否有技术原因阻止 Java 中的 final C++ 像 const 一样严格?
- 访问数据成员(本身是对象)的数据成员,就好像它们是类成员一样
- 我们可以将集合的值存储在变量中吗?就像我们可以将数组的值存储在变量中一样
- 我如何在C++像在 Python 中一样实现 f 字符串?
- 如果以下行不包含决策或分支,GDB 无法单步跳过函数
- 自制的上衣:看起来一样,但不完全相同
- 如何使用 redis-plus-plus 存储二进制数据,就像我想存储结构一样?@for_stack?
- 如何像在 C++ 中处理数组一样对 .txt 文件中的字符进行寻址?
- 如何将两个字符串加在一起,就好像它们是变量一样?
- 函数指针与条件分支
- 布尔表达式是否像使用 if 或 switch 进行分支一样繁琐?