Desde C ++ 17 se puede escribir un ifbloque 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 = falsefinal?
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_oncees una opción (se usa para enhebrar, pero aún así funciona).ifcondició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_oncesignifica 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 1evalúa!by entra por lazo,Thread 2evalúa!by entra por lazo,Thread 1hace sus cosas y se va el bucle, el establecimientob == falsede!bdecirb = true...Thread 2hace sus cosas y deja el bucle for, estableciéndolob == trueen!bieb = 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=falselo que será preferible.En C ++ 17 puedes escribir
para evitar jugar con
iel cuerpo del bucle.icomienza con 0 (garantizados por la norma), y la expresión después de los;conjuntosia1la 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
ino se filtra en el cuerpo del bucle.fuente
static int i;podría (realmente no estoy seguro) ser uno de esos casos en los queise 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::exchangeutilizando 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::exchangesegú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 lastaticpalabra clave.Si usted está preocupado por micro-optimización (como las personas que usan C ++ son a menudo), usted podría así rayar
booly utilizarinten su lugar, lo que le permitirá el uso post-decremento (o más bien, de la subasta , ya que a diferenciabooldecrementar unaintserá no saturar a cero ...):Solía ser que
booltení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 unaintmulta y, por supuesto, funcionará como una condición booleana.Bonificación: puede implementar
do_twiceodo_thricesimilar ...fuente
booly disminuir una vez. Pero el incremento funciona bien conint. Ver demostración en línea: coliru.stacked-crooked.com/a/ee83f677a9c0c85ado_onceenvuelve 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 ifuera 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
0solo una vez, cuando se ejecute el bloque, y luego el valor aumentará. Simplemente usamos el!operador para negarlo.fuente