Depuis C ++ 17, on peut écrire un if
bloc qui sera exécuté exactement une fois comme ceci:
#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;
}
}
}
Je sais que je réfléchis peut-être trop à cela, et il y a d'autres façons de résoudre cela, mais quand même - est-il possible d'écrire ceci d'une manière ou d'une autre comme ça, donc il n'y a pas besoin de do_once = false
la fin?
if (DO_ONCE) {
// Do stuff
}
Je pense à une fonction d'assistance do_once()
, contenant le static bool do_once
, mais que faire si je voulais utiliser cette même fonction à différents endroits? Serait-ce le moment et l'endroit pour un #define
? J'espère que non.
c++
if-statement
c++17
nada
la source
la source
if (i == 0)
? C'est assez clair.std::call_once
est peut - être une option (elle est utilisée pour le filetage, mais fait toujours son travail)if
condition pouvaient l'êtrestatic
. C'est malin.Réponses:
Utilisez
std::exchange
:Vous pouvez le raccourcir en inversant la valeur de vérité:
Mais si vous l'utilisez beaucoup, ne soyez pas sophistiqué et créez plutôt un wrapper:
Et utilisez-le comme:
La variable n'est pas censée être référencée en dehors de la condition, donc le nom ne nous achète pas beaucoup. En s'inspirant d'autres langages comme Python qui donnent une signification particulière à l'
_
identifiant, on peut écrire:Autres améliorations: profitez de la section BSS (@Deduplicator), évitez l'écriture en mémoire lorsque nous avons déjà exécuté (@ShadowRanger), et donnez un indice de prédiction de branche si vous allez tester plusieurs fois (par exemple comme dans la question):
la source
#define ONLY_ONCE if (static bool DO_ONCE_ = true; std::exchange(DO_ONCE_, false))
à utiliser comme:ONLY_ONCE { foo(); }
_
est utilisé dans de nombreux logiciels pour marquer des chaînes traduisibles. Attendez-vous à ce que des choses intéressantes se produisent._
pour la variable, il y aurait un-Pythonic. Vous ne l'utilisez pas_
pour les variables qui seront référencées plus tard, uniquement pour stocker des valeurs où vous devez fournir une variable mais vous n'avez pas besoin de la valeur. Il est généralement utilisé pour le déballage lorsque vous n'avez besoin que de certaines des valeurs. (Il existe d'autres cas d'utilisation, mais ils sont assez différents du cas de valeurPeut-être pas la solution la plus élégante et vous n'en voyez aucune réelle
if
, mais la bibliothèque standard couvre en fait ce cas:, voirstd::call_once
.L'avantage ici est que c'est thread-safe.
la source
call_once
de moi signifie que tu veux appeler quelque chose une fois. Fou, je sais.C ++ a une primitive de flux de contrôle intégrée qui consiste déjà en "( before-block; condition; after-block )":
Ou plus hackeur, mais plus court:
Cependant, je pense que l'une des techniques présentées ici doit être utilisée avec précaution, car elles ne sont pas (encore?) Très courantes.
la source
b == false
:Thread 1
évalue!b
et entre la boucle for,Thread 2
évalue!b
et entre la boucle for,Thread 1
fait son truc et laisse la boucle for, miseb == false
à!b
ieb = true
...Thread 2
fait son truc et laisse la boucle for, miseb == true
à!b
ieb = false
, permettant à tout le processus d'être répété indéfiniment)b=!b
, ça a l'air bien, mais vous voulez en fait que la valeur soit fausse, doncb=false
sera préférée.En C ++ 17, vous pouvez écrire
afin d'éviter de jouer avec
i
dans le corps de la boucle.i
commence par 0 (garanti par la norme), et l'expression après les;
ensemblesi
jusqu'à1
la première évaluation.Notez qu'en C ++ 11, vous pouvez réaliser la même chose avec une fonction lambda
ce qui présente également un léger avantage en ce qu'il
i
ne fuit pas dans le corps de boucle.la source
static int i;
puisse (je ne suis vraiment pas sûr) être l'un de ces cas où ili
est garanti d'être initialisé0
, il est beaucoup plus clair à utiliserstatic int i = 0;
ici.Cette solution est thread-safe (contrairement à la plupart des autres suggestions).
la source
()
déclaration lambda est facultative (si vide)?Vous pouvez encapsuler l'action ponctuelle dans le constructeur d'un objet statique que vous instanciez à la place du conditionnel.
Exemple:
Ou vous pouvez en effet vous en tenir à une macro, qui peut ressembler à ceci:
la source
Comme @damon l'a dit, vous pouvez éviter d'utiliser
std::exchange
en utilisant un entier décrémentant, mais vous devez vous rappeler que les valeurs négatives se résolvent en vrai. La façon d'utiliser ceci serait:Traduire ceci dans le wrapper sophistiqué de @ Acorn ressemblerait à ceci:
la source
Si utiliser
std::exchange
comme suggéré par @Acorn est probablement le moyen le plus idiomatique, une opération d'échange n'est pas forcément bon marché. Bien que l'initialisation statique soit bien sûr sûre pour les threads (à moins que vous disiez à votre compilateur de ne pas le faire), toute considération concernant les performances est de toute façon quelque peu futile en présence dustatic
mot - clé.Si vous êtes préoccupé par micro-optimisation (comme les personnes utilisant C ++ sont souvent), vous pouvez aussi bien gratter
bool
et utiliser à laint
place, ce qui vous permettra d'utiliser post-décrément (ou plutôt, incrément , comme la différencebool
décrémentation unint
sera pas sature à zéro ...):Il fut un temps que
bool
HAD incrément / décrément opérateurs, mais ils ont été dépréciée depuis longtemps (C ++ 11? Pas sûr?) Et sont à éliminer complètement en C ++ 17. Néanmoins, vous pouvez décrémenter uneint
amende juste, et cela fonctionnera bien sûr comme une condition booléenne.Bonus: vous pouvez mettre en œuvre
do_twice
ou dedo_thrice
manière similaire ...la source
bool
et décrémentait une fois. Mais l'incrément fonctionne bien avecint
. Voir la démo en ligne: coliru.stacked-crooked.com/a/ee83f677a9c0c85ado_once
s'enroule et finira par atteindre 0 à nouveau (et encore et encore ...).Basé sur l'excellente réponse de @ Bathsheba à ce sujet, c'est encore plus simple.
Dans
C++ 17
, vous pouvez simplement faire:(Dans les versions précédentes, déclarez simplement
int i
dehors du bloc. Fonctionne également en C :)).En termes simples: vous déclarez i, qui prend la valeur par défaut de zéro (
0
). Zéro est faux, nous utilisons donc l'!
opérateur point d'exclamation ( ) pour l'annuler. On prend alors en compte la propriété increment de la<ID>++
opérateur, qui est d'abord traitée (assignée, etc.) puis incrémentée.Par conséquent, dans ce bloc, i sera initialisé et n'aura la valeur
0
qu'une seule fois, lorsque le bloc sera exécuté, puis la valeur augmentera. Nous utilisons simplement l'!
opérateur pour l'annuler.la source