Desde C ++ 17 se puede escribir un if
bloque que se ejecutará exactamente una vez como esta:
#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;
}
}
}
Sé que podría estar pensando demasiado en esto, y hay otras formas de resolver esto, pero aún así, ¿es posible escribir esto de alguna manera como esta, por lo que no hay necesidad del do_once = false
final?
if (DO_ONCE) {
// Do stuff
}
Estoy pensando en una función auxiliar do_once()
, que contiene el static bool do_once
, pero ¿qué pasa si quisiera usar esa misma función en diferentes lugares? ¿Podría ser este el momento y el lugar para un #define
? Espero que no.
c++
if-statement
c++17
nada
fuente
fuente
if (i == 0)
? Está lo suficientemente claro.std::call_once
es una opción (se usa para enhebrar, pero aún así funciona).if
condición podrían serstatic
. Eso es inteligente.Respuestas:
Uso
std::exchange
:Puede acortarlo invirtiendo el valor de verdad:
Pero si está usando esto mucho, no sea elegante y cree un envoltorio en su lugar:
Y úsalo como:
No se debe hacer referencia a la variable fuera de la condición, por lo que el nombre no nos compra mucho. Inspirándonos en otros lenguajes como Python que le dan un significado especial al
_
identificador, podemos escribir:Mejoras adicionales: aproveche la sección BSS (@Deduplicator), evite la escritura en la memoria cuando ya la hemos ejecutado (@ShadowRanger) y brinde una sugerencia de predicción de rama si va a realizar pruebas muchas veces (por ejemplo, como en la pregunta):
fuente
#define ONLY_ONCE if (static bool DO_ONCE_ = true; std::exchange(DO_ONCE_, false))
para ser utilizado como:ONLY_ONCE { foo(); }
_
se usa en muchos programas para marcar cadenas traducibles. Esperar que sucedan cosas interesantes._
para la variable no habría Pythonic. No se utiliza_
para las variables que se hará referencia más adelante, sólo para almacenar valores en la que tiene que proporcionar una variable, pero que no es necesario el valor. Por lo general, se usa para desempaquetar cuando solo necesita algunos de los valores. (Hay otros casos de uso, pero son bastante diferentes del caso del valor de usar y tirar).Quizás no sea la solución más elegante y no vea ninguna real
if
, pero la biblioteca estándar en realidad cubre este caso :, veastd::call_once
.La ventaja aquí es que esto es seguro para subprocesos.
fuente
call_once
significa que quieres llamar a algo una vez. Loco, lo sé.C ++ tiene una primitiva de flujo de control integrada que consiste en "( antes del bloque; condición; después del bloque )" ya:
O más pirateado, pero más corto:
Sin embargo, creo que cualquiera de las técnicas presentadas aquí debe usarse con cuidado, ya que no son (¿todavía?) Muy comunes.
fuente
b == false
:Thread 1
evalúa!b
y entra por lazo,Thread 2
evalúa!b
y entra por lazo,Thread 1
hace sus cosas y se va el bucle, el establecimientob == false
de!b
decirb = true
...Thread 2
hace sus cosas y deja el bucle for, estableciéndolob == true
en!b
ieb = false
, permitiendo que todo el proceso se repita indefinidamente)b=!b
, eso se ve bien, pero en realidad quieres que el valor sea falso, porb=false
lo que será preferible.En C ++ 17 puedes escribir
para evitar jugar con
i
el cuerpo del bucle.i
comienza con 0 (garantizados por la norma), y la expresión después de los;
conjuntosi
a1
la primera vez que se evalúa.Tenga en cuenta que en C ++ 11 podría lograr lo mismo con una función lambda
lo que también conlleva una ligera ventaja ya que
i
no se filtra en el cuerpo del bucle.fuente
static int i;
podría (realmente no estoy seguro) ser uno de esos casos en los quei
se garantiza que se inicializará0
, es mucho más claro usarlostatic int i = 0;
aquí.Esta solución es segura para subprocesos (a diferencia de muchas de las otras sugerencias).
fuente
()
es opcional (si está vacío) en la declaración lambda?Puede ajustar la acción de una sola vez en el constructor de un objeto estático que instancia en lugar del condicional.
Ejemplo:
O, de hecho, puede quedarse con una macro, que puede verse así:
fuente
Como dijo @damon, puede evitar el uso
std::exchange
utilizando un número entero decreciente, pero debe recordar que los valores negativos se resuelven en verdaderos. La forma de usar esto sería:Traducir esto al elegante envoltorio de @ Acorn se vería así:
fuente
Si bien usar
std::exchange
según lo sugerido por @Acorn es probablemente la forma más idiomática, una operación de intercambio no es necesariamente barata. Aunque, por supuesto, se garantiza que la inicialización estática sea segura para subprocesos (a menos que le indique a su compilador que no lo haga), por lo que cualquier consideración sobre el rendimiento es algo inútil de todos modos en presencia de lastatic
palabra clave.Si usted está preocupado por micro-optimización (como las personas que usan C ++ son a menudo), usted podría así rayar
bool
y utilizarint
en su lugar, lo que le permitirá el uso post-decremento (o más bien, de la subasta , ya que a diferenciabool
decrementar unaint
será no saturar a cero ...):Solía ser que
bool
tenía operadores de incremento / decremento, pero fueron desaprobados hace mucho tiempo (C ++ 11? No estoy seguro?) Y deben eliminarse por completo en C ++ 17. Sin embargo, puede disminuir unaint
multa y, por supuesto, funcionará como una condición booleana.Bonificación: puede implementar
do_twice
odo_thrice
similar ...fuente
bool
y disminuir una vez. Pero el incremento funciona bien conint
. Ver demostración en línea: coliru.stacked-crooked.com/a/ee83f677a9c0c85ado_once
envuelve y eventualmente golpeará 0 nuevamente (y una y otra vez ...).Basado en la gran respuesta de @ Bathsheba para esto, simplemente lo hizo aún más simple.
En
C++ 17
, simplemente puedes hacer:(En versiones anteriores, simplemente declara
int i
fuera del bloque. También funciona en C :)).En palabras simples: declaras i, que toma el valor predeterminado de cero (
0
). Zero es falsey, por lo tanto, utilizamos el!
operador de signo de exclamación ( ) para negarlo. Luego tomamos en cuenta la propiedad de incremento del<ID>++
operador, que primero se procesa (asigna, etc.) y luego se incrementa.Por lo tanto, en este bloque, se inicializará y tendré el valor
0
solo una vez, cuando se ejecute el bloque, y luego el valor aumentará. Simplemente usamos el!
operador para negarlo.fuente