Seit C ++ 17 kann man einen if
Block schreiben , der genau einmal so ausgeführt wird:
#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;
}
}
}
Ich weiß, dass ich das vielleicht überdenke, und es gibt andere Möglichkeiten, dies zu lösen, aber dennoch - ist es möglich, dies irgendwie so zu schreiben, so dass es do_once = false
am Ende nicht nötig ist ?
if (DO_ONCE) {
// Do stuff
}
Ich denke an eine Hilfsfunktion, do_once()
, die die enthält static bool do_once
, aber was ist, wenn ich dieselbe Funktion an verschiedenen Orten verwenden möchte? Könnte dies die Zeit und der Ort für eine sein #define
? Ich hoffe nicht.
c++
if-statement
c++17
Nada
quelle
quelle
if (i == 0)
? Es ist klar genug.std::call_once
ist es eine Option (es wird zum Einfädeln verwendet, macht aber trotzdem seinen Job).if
Bedingungen initialisiert wurden , sein könntenstatic
. Das ist schlau.Antworten:
Verwendung
std::exchange
:Sie können es kürzer machen, indem Sie den Wahrheitswert umkehren:
Aber wenn Sie dies häufig verwenden, seien Sie nicht schick und erstellen Sie stattdessen einen Wrapper:
Und benutze es wie:
Die Variable sollte nicht außerhalb der Bedingung referenziert werden, daher kauft uns der Name nicht viel. Wir lassen uns von anderen Sprachen wie Python inspirieren , die dem
_
Bezeichner eine besondere Bedeutung geben , und schreiben möglicherweise:Weitere Verbesserungen: Nutzen Sie den BSS-Abschnitt (@Deduplicator), vermeiden Sie das Schreiben des Speichers, wenn wir ihn bereits ausgeführt haben (@ShadowRanger), und geben Sie einen Hinweis zur Verzweigungsvorhersage, wenn Sie mehrmals testen möchten (z. B. wie in der Frage):
quelle
#define ONLY_ONCE if (static bool DO_ONCE_ = true; std::exchange(DO_ONCE_, false))
Zu verwenden als:ONLY_ONCE { foo(); }
_
wird in vielen Programmen verwendet, um übersetzbare Zeichenfolgen zu markieren. Erwarten Sie interessante Dinge._
der Variablen wäre unpythonisch. Sie verwenden nicht_
für Variablen , die später Bezug genommen wird, nur zum Speichern von Werten , wo Sie haben eine Variable zur Verfügung zu stellen , aber sie nicht den Wert benötigen. Es wird normalerweise zum Auspacken verwendet, wenn Sie nur einige der Werte benötigen. (Es gibt andere Anwendungsfälle, aber sie unterscheiden sichVielleicht nicht die eleganteste Lösung, und Sie sehen keine tatsächliche
if
, aber die Standardbibliothek deckt diesen Fall tatsächlich abstd::call_once
.Der Vorteil hierbei ist, dass dies threadsicher ist.
quelle
call_once
für mich zu sorgen bedeutet, dass Sie einmal etwas anrufen möchten. Verrückt, ich weiß.C ++ verfügt bereits über ein integriertes Kontrollflussprimitiv, das aus "( vor dem Block; Bedingung; nach dem Block )" besteht:
Oder hackiger, aber kürzer:
Ich denke jedoch, dass jede der hier vorgestellten Techniken mit Vorsicht angewendet werden sollte, da sie (noch?) Nicht sehr häufig sind.
quelle
b == false
:Thread 1
wertet die!b
Schleife aus und gibt sie ein ,Thread 2
wertet die!b
Schleife aus undThread 1
gibt sie ein , erledigt ihre Aufgaben und verlässt die for-Schleife und setzt sieb == false
auf!b
ieb = true
...Thread 2
macht seine Sachen und verlässt die for-Schleife, setztb == true
auf!b
dhb = false
, damit der gesamte Prozess auf unbestimmte Zeit wiederholt werden kann)b=!b
, das sieht gut aus, aber Sie möchten tatsächlich, dass der Wert falsch ist, alsob=false
werden Sie bevorzugt.In C ++ 17 können Sie schreiben
um nicht
i
im Loop-Body herumzuspielen.i
beginnt mit 0 (vom Standard garantiert) und der Ausdruck nach den;
Sätzen wirdi
zum1
ersten Mal ausgewertet.Beachten Sie, dass Sie in C ++ 11 dasselbe mit einer Lambda-Funktion erreichen können
Dies hat auch den leichten Vorteil, dass
i
es nicht in den Schleifenkörper gelangt.quelle
static int i;
(ich bin mir wirklich nicht sicher) einer der Fälle sein kann, in deneni
eine Initialisierung garantiert ist0
, ist die Verwendungstatic int i = 0;
hier viel klarer .Diese Lösung ist threadsicher (im Gegensatz zu vielen anderen Vorschlägen).
quelle
()
Lambda-Deklaration optional ist (falls leer)?Sie können die einmalige Aktion in den Konstruktor eines statischen Objekts einschließen, das Sie anstelle der Bedingung instanziieren.
Beispiel:
Oder Sie bleiben tatsächlich bei einem Makro, das ungefähr so aussieht:
quelle
Wie @damon sagte, können Sie die Verwendung vermeiden,
std::exchange
indem Sie eine dekrementierende Ganzzahl verwenden, aber Sie müssen sich daran erinnern, dass negative Werte in true aufgelöst werden. Der Weg, dies zu benutzen, wäre:Die Übersetzung in @ Acorns ausgefallenen Wrapper würde folgendermaßen aussehen:
quelle
Während die Verwendung
std::exchange
wie von @Acorn vorgeschlagen wahrscheinlich die idiomatischste Methode ist, ist eine Austauschoperation nicht unbedingt billig. Obwohl die statische Initialisierung garantiert threadsicher ist (es sei denn, Sie weisen Ihren Compiler an, dies nicht zu tun), sind Überlegungen zur Leistung bei Vorhandensein desstatic
Schlüsselworts ohnehin vergeblich .Wenn Sie sich Gedanken über die Mikrooptimierung machen (wie dies bei C ++ häufig der Fall ist), können Sie diese auch kratzen
bool
und verwendenint
, um die Nachdekrementierung (oder besser gesagt die Inkrementierung) zu verwenden , im Gegensatz zurbool
Dekrementierung undint
wird nicht auf Null gesättigt ...):Früher gab es
bool
Inkrementierungs- / Dekrementierungsoperatoren, aber diese waren längst veraltet (C ++ 11? Nicht sicher?) Und müssen in C ++ 17 vollständig entfernt werden. Trotzdem können Sie eineint
Geldstrafe dekrementieren , und es wird natürlich als boolesche Bedingung funktionieren.Bonus: Sie können implementieren
do_twice
oderdo_thrice
ähnlich ...quelle
bool
und dekrementierte es einmal. Aber Inkrement funktioniert gut mitint
. Siehe Online-Demo: coliru.stacked-crooked.com/a/ee83f677a9c0c85ado_once
umgibt und schließlich wieder 0 trifft (und immer wieder ...).Basierend auf @ Bathshebas großartiger Antwort darauf - hat es nur noch einfacher gemacht.
In
C++ 17
können Sie einfach tun:(In früheren Versionen deklarieren Sie einfach
int i
außerhalb des Blocks. Funktioniert auch in C :)).In einfachen Worten: Sie deklarieren i, was den Standardwert Null (
0
) annimmt . Null ist falsch, daher verwenden wir den Ausrufezeichen (!
), um sie zu negieren. Wir berücksichtigen dann die Inkrement-Eigenschaft des<ID>++
Operators, die zuerst verarbeitet (zugewiesen usw.) und dann inkrementiert wird.Daher wird in diesem Block i initialisiert und hat den Wert
0
nur einmal, wenn der Block ausgeführt wird, und dann erhöht sich der Wert. Wir benutzen einfach den!
Operator, um es zu negieren.quelle