Od C ++ 17 można napisać if
blok, który zostanie wykonany dokładnie raz w ten sposób:
#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;
}
}
}
Wiem, że mogę się nad tym zastanawiać i są inne sposoby rozwiązania tego problemu, ale nadal - czy można to jakoś napisać w ten sposób, więc do_once = false
na końcu nie ma potrzeby ?
if (DO_ONCE) {
// Do stuff
}
Myślę o funkcji pomocniczej do_once()
zawierającej static bool do_once
, ale co jeśli chciałbym użyć tej samej funkcji w różnych miejscach? Czy to może być czas i miejsce na spotkanie #define
? Mam nadzieję, że nie.
c++
if-statement
c++17
nada
źródło
źródło
if (i == 0)
? To wystarczająco jasne.std::call_once
jest opcją (jest używana do gwintowania, ale nadal działa).if
warunkach mogą byćstatic
. To sprytne.Odpowiedzi:
Zastosowanie
std::exchange
:Możesz go skrócić, odwracając wartość prawdy:
Ale jeśli często tego używasz, nie bądź wyszukany i zamiast tego utwórz opakowanie:
I używaj go jak:
Do zmiennej nie należy odwoływać się poza warunkiem, więc nazwa niewiele nas kupuje. Czerpiąc inspirację z innych języków, takich jak Python, które nadają
_
identyfikatorowi specjalne znaczenie , możemy napisać:Dalsze ulepszenia: skorzystaj z sekcji BSS (@Deduplicator), unikaj zapisu do pamięci, gdy już uruchomiliśmy (@ShadowRanger) i daj wskazówkę dotyczącą przewidywania gałęzi, jeśli zamierzasz testować wiele razy (np. Jak w pytaniu):
źródło
#define ONLY_ONCE if (static bool DO_ONCE_ = true; std::exchange(DO_ONCE_, false))
Do użycia jako:ONLY_ONCE { foo(); }
_
jest używana w wielu programach do oznaczania przetłumaczalnych ciągów. Spodziewaj się ciekawych rzeczy._
zmiennej byłoby nie-Pythonic. Nie używasz_
dla zmiennych, do których będziemy się później odwoływać, tylko do przechowywania wartości, w przypadku których musisz podać zmienną, ale nie potrzebujesz wartości. Zwykle jest używany do rozpakowywania, gdy potrzebujesz tylko niektórych wartości. (Istnieją inne przypadki użycia, ale są one zupełnie inne niż przypadek wartości jednorazowej).Może nie jest to najbardziej eleganckie rozwiązanie i nie widzisz żadnego rzeczywistego
if
, ale standardowa biblioteka faktycznie obejmuje ten przypadek: patrzstd::call_once
.Zaletą jest to, że jest to bezpieczne dla wątków.
źródło
call_once
mnie oznacza, że chcesz kiedyś do czegoś zadzwonić. Szalone, wiem.C ++ ma wbudowaną operację podstawową przepływu sterowania, która już składa się z „( przed blokiem; warunek; po bloku )”:
Lub trudniejszy, ale krótszy:
Uważam jednak, że każdej z przedstawionych tu technik należy używać ostrożnie, ponieważ nie są (jeszcze?) Bardzo powszechne.
źródło
b == false
:Thread 1
oblicza!b
i wchodzi do pętli for,Thread 2
ocenia!b
i wchodzi do pętli for,Thread 1
wykonuje swoją pracę i pozostawia pętlę for, ustawiającb == false
na!b
ieb = true
...Thread 2
robi swoje i pozostawia pętlę for, ustawiającb == true
na!b
np.b = false
, pozwalając na powtarzanie całego procesu w nieskończoność)b=!b
, to wygląda ładnie, ale tak naprawdę chcesz, aby wartość była fałszywa, więcb=false
powinna być preferowana.W C ++ 17 możesz pisać
aby uniknąć zabawy z
i
korpusem pętli.i
zaczyna się od 0 (gwarantowane przez standard), a wyrażenie po;
zestawachi
do1
pierwszej oceny.Zauważ, że w C ++ 11 możesz osiągnąć to samo dzięki funkcji lambda
co również ma niewielką zaletę polegającą na tym, że
i
nie przecieka do korpusu pętli.źródło
static int i;
może (naprawdę nie jestem pewien) być jednym z tych przypadków, w którychi
na pewno zostanie zainicjowany0
, jest o wiele jaśniejszy w użyciustatic int i = 0;
tutaj.To rozwiązanie jest bezpieczne dla wątków (w przeciwieństwie do wielu innych sugestii).
źródło
()
jest opcjonalne (jeśli puste) w deklaracji lambda?Możesz opakować jednorazową akcję w konstruktorze statycznego obiektu, którego instancję tworzysz zamiast warunku.
Przykład:
Lub możesz trzymać się makra, które może wyglądać mniej więcej tak:
źródło
Jak powiedział @damon, możesz uniknąć używania
std::exchange
zmniejszającej się liczby całkowitej, ale musisz pamiętać, że wartości ujemne są rozwiązywane na prawdę. Sposób na to byłby:Tłumaczenie tego na fantazyjne opakowanie @ Acorn wyglądałoby tak:
źródło
O ile korzystanie
std::exchange
zgodnie z sugestią @Acorn jest chyba najbardziej idiomatycznym sposobem, operacja wymiany niekoniecznie jest tania. Chociaż oczywiście statyczna inicjalizacja jest gwarantowana jako bezpieczna wątkowo (chyba że powiesz kompilatorowi, aby tego nie robił), więc wszelkie rozważania dotyczące wydajności są i tak daremne w obecnościstatic
słowa kluczowego.Jeśli obawiasz się o mikro optymalizacji (jako osób korzystających z C ++ często są), można także zarysować
bool
i używaćint
zamiast, który pozwoli Ci korzystać z post-ubytek (lub raczej przyrost , jak w przeciwieństwiebool
zmniejszanieint
będzie nie nasycić do zera ...):Kiedyś
bool
miały operatory inkrementacji / dekrementacji, ale zostały one przestarzałe dawno temu (C ++ 11? Nie jesteś pewien?) I mają zostać całkowicie usunięte w C ++ 17. Niemniej jednak możesz zmniejszyćint
sprawiedliwą karę i będzie to oczywiście działało jako warunek logiczny.Bonus: możesz wdrożyć
do_twice
lubdo_thrice
podobnie ...źródło
bool
i zmniejszało się. Ale przyrost działa dobrze zint
. Zobacz demo online: coliru.stacked-crooked.com/a/ee83f677a9c0c85ado_once
zawija się i ostatecznie ponownie osiągnie 0 (i znowu i znowu ...).W oparciu o świetną odpowiedź @ Bathsheby - po prostu uczyniłem to jeszcze prostszym.
W
C++ 17
możesz po prostu:(W poprzednich wersjach po prostu zadeklaruj
int i
poza blokiem. Działa również w C :)).W prostych słowach: deklarujesz i, które przyjmuje domyślną wartość zero (
0
). Zero to fałsz, dlatego używamy!
operatora wykrzyknika ( ), aby go zanegować. Następnie bierzemy pod uwagę właściwość inkrementacji<ID>++
operatora, która najpierw jest przetwarzana (przypisywana itp.), A następnie zwiększana.Dlatego w tym bloku zostanie zainicjowany i będzie miał wartość
0
tylko raz, kiedy blok zostanie wykonany, a następnie wartość wzrośnie. Po prostu używamy!
operatora, aby to zanegować.źródło