nad*_*ada 134 c++ if-statement c++17
由于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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我知道我可能对此有过多的思考,还有其他方法可以解决此问题,但是仍然-是否可以以这种方式编写此文本,因此do_once = false最后不需要?
if (DO_ONCE) {
// Do stuff
}
Run Code Online (Sandbox Code Playgroud)
我在考虑一个辅助函数,do_once()其中包含static bool do_once,但是如果我想在不同的地方使用相同的函数呢?可能这是时间和地点#define吗?我希望不是。
Aco*_*orn 142
if (static bool do_once = true; std::exchange(do_once, false))
Run Code Online (Sandbox Code Playgroud)
您可以更短一些来反转真实值:
if (static bool do_once; !std::exchange(do_once, true))
Run Code Online (Sandbox Code Playgroud)
但是,如果您经常使用这种方法,请不要幻想并创建一个包装器:
struct Once {
bool b = true;
explicit operator bool() { return std::exchange(b, false); }
};
Run Code Online (Sandbox Code Playgroud)
并像这样使用它:
if (static Once once; once)
Run Code Online (Sandbox Code Playgroud)
不应在条件之外引用该变量,因此名称对我们没有多大帮助。从其他语言(例如Python)中获得启发,这些语言赋予_标识符特殊的含义,我们可以这样写:
if (static Once _; _)
Run Code Online (Sandbox Code Playgroud)
进一步的改进:利用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;
}
};
Run Code Online (Sandbox Code Playgroud)
lub*_*bgr 91
也许不是最优雅的解决方案,并且您看不到任何实际的解决方案if,但是标准库实际上涵盖了这种情况:请参阅std::call_once。
#include <mutex>
std::once_flag flag;
for (int i = 0; i < 10; ++i)
std::call_once(flag, [](){ std::puts("once\n"); });
Run Code Online (Sandbox Code Playgroud)
这样做的好处是这是线程安全的。
Seb*_*ach 51
C ++确实具有一个内置的控制流原语,该原语已经由“((before-block; condition; after-block)”)组成:
for (static bool b = true; b; b = false)
Run Code Online (Sandbox Code Playgroud)
或更骇客,但更短:
for (static bool b; !b; b = !b)
Run Code Online (Sandbox Code Playgroud)
但是,我认为此处介绍的任何技术都应谨慎使用,因为它们(不是吗)非常普遍。
Bat*_*eba 29
在C ++ 17中,您可以编写
if (static int i; i == 0 && (i = 1)){
Run Code Online (Sandbox Code Playgroud)
为了避免i在循环体内玩弄。i从0开始(由标准保证的),并且后表达;集i到1第一次它被评估。
请注意,在C ++ 11中,您可以使用lambda函数实现相同的功能
if ([]{static int i; return i == 0 && (i = 1);}()){
Run Code Online (Sandbox Code Playgroud)
这也具有一点优势,即i不会泄漏到回路主体中。
Wax*_*rat 14
static bool once = [] {
std::cout << "Hello one-shot\n";
return false;
}();
Run Code Online (Sandbox Code Playgroud)
此解决方案是线程安全的(与许多其他建议不同)。
您可以将一次性操作包装在实例化的静态对象的构造函数中,以代替条件对象。
例:
#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 << "once\n"; });
std::cout << "Hello World\n";
}
}
Run Code Online (Sandbox Code Playgroud)
或者您可能确实坚持使用宏,看起来可能像这样:
#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 << "once\n");
std::cout << "Hello World\n";
}
}
Run Code Online (Sandbox Code Playgroud)
小智 8
Like @damon said, you can avoid using std::exchange by using a decrementing integer, but you have to remember that negative values resolve to true. The way to use this would be:
if (static int n_times = 3; n_times && n_times--)
{
std::cout << "Hello world x3" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
Translating this to @Acorn's fancy wrapper would look like this:
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;
}
Run Code Online (Sandbox Code Playgroud)
虽然std::exchange按@Acorn的建议使用是最惯用的方式,但交换操作不一定便宜。尽管当然可以保证静态初始化是线程安全的(除非您告诉编译器不要这样做),所以在存在static关键字的情况下,任何有关性能的考虑都是徒劳的。
如果你关注微优化(如使用C ++往往是人),你可能还有划伤bool和使用int代替,这将允许您使用后递减(或更确切地说,增量,因为不像bool递减的int将不饱和为零...):
if(static int do_once = 0; !do_once++)
Run Code Online (Sandbox Code Playgroud)
过去bool曾经有增减运算符,但是很早以前就已弃用它们(C ++ 11?不确定吗?),并且在C ++ 17中将其完全删除。不过,您可以递减int就可以了,它当然可以作为布尔条件工作。
奖励:您可以实施do_twice或do_thrice类似地...
| 归档时间: |
|
| 查看次数: |
16291 次 |
| 最近记录: |