写单发'if'最优雅的方式
Most elegant way to write a one-shot 'if'
从C++ 17 开始,可以编写一个if
块,该块将像这样执行一次:
#include <iostream>
int main() {
for (unsigned i = 0; i < 10; ++i) {
if (static bool do_once = true; do_once) { // Enter only once
std::cout << "hello one-shot" << std::endl;
// Possibly much more code
do_once = false;
}
}
}
我知道我可能想多了,还有其他方法可以解决这个问题,但仍然 - 是否有可能以某种方式这样写这个,所以最后不需要do_once = false
?
if (DO_ONCE) {
// Do stuff
}
我正在考虑一个包含static bool do_once
的辅助函数,do_once()
,但是如果我想在不同的地方使用相同的函数怎么办?这可能是#define
的时间和地点吗?我希望不会。
使用std::exchange
:
if (static bool do_once = true; std::exchange(do_once, false))
您可以缩短它以反转真值:
if (static bool do_once; !std::exchange(do_once, true))
但是,如果您经常使用它,请不要花哨并创建一个包装器:
struct Once {
bool b = true;
explicit operator bool() { return std::exchange(b, false); }
};
并像这样使用它:
if (static Once once; once)
变量不应该在条件之外被引用,所以这个名字并没有给我们带来太多好处。从其他语言(如Python)中汲取灵感,这些语言赋予_
标识符特殊含义,我们可以这样写:
if (static Once _; _)
进一步的改进:利用 BSS 部分 (@Deduplicator),避免在我们已经运行时写入内存 (@ShadowRanger),如果您要多次测试(例如,如问题中所示),请给出分支预测提示:
// GCC, Clang, icc only; use [[likely]] in C++20 instead
#define likely(x) __builtin_expect(!!(x), 1)
struct Once {
bool b = false;
explicit operator bool()
{
if (likely(b))
return false;
b = true;
return true;
}
};
也许不是最优雅的解决方案,你看不到任何实际的if
,但标准库实际上涵盖了这种情况:,请参阅std::call_once
。
#include <mutex>
std::once_flag flag;
for (int i = 0; i < 10; ++i)
std::call_once(flag, [](){ std::puts("oncen"); });
这里的优点是这是线程安全的。
C++确实有一个内置的控制流原语,它由"(之前块;条件;后块)"已经:
for (static bool b = true; b; b = false)
或者更黑客,但更短:
for (static bool b; !b; b = !b)
但是,我认为这里介绍的任何技术都应该谨慎使用,因为它们(还?)不是很常见。
在 C++17 中你可以写
if (static int i; i == 0 && (i = 1)){
以避免在循环体中玩弄i
。i
以 0 开头(由标准保证),;
后的表达式i
设置为首次计算时1
。
请注意,在 C++11 中,您可以使用 lambda 函数实现相同的目标
。if ([]{static int i; return i == 0 && (i = 1);}()){
这也具有一个轻微的优势,即i
不会泄漏到环体中。
static bool once = [] {
std::cout << "Hello one-shotn";
return false;
}();
此解决方案是线程安全的(与许多其他建议不同)。
您可以将一次性操作包装在静态对象的构造函数中,该构造函数实例化以代替条件。
例:
#include <iostream>
#include <functional>
struct do_once {
do_once(std::function<void(void)> fun) {
fun();
}
};
int main()
{
for (int i = 0; i < 3; ++i) {
static do_once action([](){ std::cout << "oncen"; });
std::cout << "Hello Worldn";
}
}
或者你可能确实坚持使用宏,它可能看起来像这样:
#include <iostream>
#define DO_ONCE(exp)
do {
static bool used_before = false;
if (used_before) break;
used_before = true;
{ exp; }
} while(0)
int main()
{
for (int i = 0; i < 3; ++i) {
DO_ONCE(std::cout << "oncen");
std::cout << "Hello Worldn";
}
}
就像@damon说的,你可以通过使用递减的整数来避免使用std::exchange
,但你必须记住负值解析为true。使用它的方法是:
if (static int n_times = 3; n_times && n_times--)
{
std::cout << "Hello world x3" << std::endl;
}
将其转换为@Acorn的花哨包装器将如下所示:
struct n_times {
int n;
n_times(int number) {
n = number;
};
explicit operator bool() {
return n && n--;
};
};
...
if(static n_times _(2); _)
{
std::cout << "Hello world twice" << std::endl;
}
虽然按照@Acorn的建议使用std::exchange
可能是最惯用的方式,但交换操作不一定便宜。当然,静态初始化是保证线程安全的(除非你告诉你的编译器不要这样做),所以在存在static
关键字的情况下,任何关于性能的考虑都是徒劳的。
如果您担心微优化(就像使用C++的人经常一样),您也可以划伤bool
并使用int
,这将允许您使用后递减(或者更确切地说,递增,因为与递减不同bool
递减int
不会饱和到零......
if(static int do_once = 0; !do_once++)
过去,bool
有递增/递减运算符,但它们很久以前就被弃用了(C++11?不确定?),并将在C++17中完全删除。尽管如此,你可以很好地递减int
,它当然可以作为布尔条件工作。
奖励:您可以类似地实现do_twice
或do_thrice
...
基于@Bathsheba对此的出色回答 - 只是让它更简单。
在C++ 17
中,您可以简单地执行以下操作:
if (static int i; !i++) {
cout << "Execute once";
}
(在以前的版本中,只需在块外声明int i
。也适用于 C :))。
简单来说:你声明 i,默认值为零 (0
)。 零是假的,因此我们使用感叹号(!
)运算符来否定它。 然后,我们考虑<ID>++
运算符的增量属性,该属性首先被处理(赋值等),然后递增。
因此,在这个块中,i 将被初始化并仅0
一次值,当块被执行时,然后值将增加。我们只是使用!
运算符来否定它。
- 如何在c++中为模板函数实例创建快捷方式
- 我的简单if-else语句是如何无法访问的代码
- 如何将enable-if与模板参数和参数包一起使用
- 无论条件是否为true,if总是在c++中执行
- Arduino:for/while/if在void setup()或void loop()之前?——错误:之前需要不合格
- 在c代码之间共享数据的最佳方式
- 在C++中将函数压缩为两种方式
- 以螺旋方式打印矩阵的程序.(工作不好)
- Insert函数不适用于2 if语句C++
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- If语句未被求值C++
- C++嵌套if语句,基本货币交换
- 多个If语句与使用逻辑运算符计算条件的单个语句的比较
- 创建引用向量的优雅方式
- 而不是那么多的 if 语句,我想要一个逻辑,我可以用一个语句或优化的方式来完成
- 我如何在一个 if 语句中声明所有数字我尝试通过其他方式声明所有数字,如果一个接一个,但似乎代码有逻辑错误
- 编写多个 If 语句的优雅方式
- 写单发'if'最优雅的方式
- 优雅的"if(T t = ...) { } else return t;"方式?
- 以某种方式将 If/Else's 转换为开关