C++ – Most elegant way to write a one-shot ‘if’

cc++17if statement

Since C++ 17 one can write an if block that will get executed exactly once like this:

#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;
        }

    }
}

I know I might be overthinking this, and there are other ways to solve this, but still – is it possible to write this somehow like this, so there is no need of the do_once = false at the end?

if (DO_ONCE) {
    // Do stuff
}

I'm thinking a helper function, do_once(), containing the static bool do_once, but what if I wanted to use that same function in different places? Might this be the time and place for a #define? I hope not.

Best Answer

Use std::exchange:

if (static bool do_once = true; std::exchange(do_once, false))

You can make it shorter reversing the truth value:

if (static bool do_once; !std::exchange(do_once, true))

But if you are using this a lot, don't be fancy and create a wrapper instead:

struct Once {
    bool b = true;
    explicit operator bool() { return std::exchange(b, false); }
};

And use it like:

if (static Once once; once)

The variable is not supposed to be referenced outside the condition, so the name does not buy us much. Taking inspiration from other languages like Python which give a special meaning to the _ identifier, we may write:

if (static Once _; _)

Further improvements: take advantage of the BSS section (@Deduplicator), avoid the memory write when we have already run (@ShadowRanger), and give a branch prediction hint if you are going to test many times (e.g. like in the question):

// 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;
    }
};
Related Topic