[c++] 원샷 ‘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
}

do_once()포함 하는 도우미 함수를 생각하고 static bool 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)

변수는 조건 외부에서 참조되지 않아야하므로 이름은 우리를 많이 사지 않습니다. 식별자와 관련 하여 파이썬 과 같은 다른 언어에서 영감을 받아 다음과 같이 _작성할 수 있습니다.

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("once\n"); });

여기서 장점은 스레드 안전하다는 것입니다.


답변

C ++에는 이미 “( before-block; condition; after-block )”으로 구성된 제어 흐름 기본 요소가 내장되어 있습니다 .

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으로 시작하고, 이후 발현 ;세트 i1그 평가 처음.

C ++ 11에서는 람다 함수를 사용하여 동일한 결과를 얻을 수 있습니다.

if ([]{static int i; return i == 0 && (i = 1);}()){

i루프 몸체에 누출되지 않는 장점도 있습니다 .


답변

static bool once = [] {
  std::cout << "Hello one-shot\n";
  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 << "once\n"; });
        std::cout << "Hello World\n";
    }
}

또는 실제로 매크로를 사용하면 다음과 같이 보일 수 있습니다.

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


답변

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