Desde o C ++ 17, é possível escrever um if
bloco que será executado exatamente uma vez assim:
#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;
}
}
}
Eu sei que posso estar pensando demais nisso, e há outras maneiras de resolver isso, mas ainda assim - é possível escrever isso de alguma forma assim, para que não haja necessidade de do_once = false
no final?
if (DO_ONCE) {
// Do stuff
}
Estou pensando em uma função auxiliar do_once()
, contendo o static bool do_once
, mas e se eu quisesse usar a mesma função em lugares diferentes? Pode ser esse o momento e o local para um #define
? Espero que não.
c++
if-statement
c++17
nada
fonte
fonte
if (i == 0)
? Está claro o suficiente.std::call_once
seja uma opção (é usada para segmentar, mas ainda funciona).if
condição poderiam serstatic
. Isso é esperto.Respostas:
Use
std::exchange
:Você pode diminuir a reversão do valor verdade:
Mas se você estiver usando muito isso, não seja chique e crie um wrapper:
E use-o como:
A variável não deve ser referenciada fora da condição, portanto o nome não nos compra muito. Inspirando-se em outras linguagens como Python, que dão um significado especial ao
_
identificador, podemos escrever:Outras melhorias: tire proveito da seção BSS (@Duplicator), evite a gravação na memória quando já executamos (@ShadowRanger) e dê uma dica de previsão de ramificação se você for testar várias vezes (por exemplo, como na pergunta):
fonte
#define ONLY_ONCE if (static bool DO_ONCE_ = true; std::exchange(DO_ONCE_, false))
Para ser usado como:ONLY_ONCE { foo(); }
_
é usado em vários softwares para marcar seqüências de caracteres traduzíveis. Espere que coisas interessantes aconteçam._
a variável, haveria não-Pythonic. Você não usa_
para variáveis que serão referenciadas posteriormente, apenas para armazenar valores nos quais você precisa fornecer uma variável, mas não precisa do valor. Geralmente é usado para descompactar quando você precisa apenas de alguns valores. (Há outros casos de uso, mas eles são muito diferente do caso valor descartável.)Talvez não seja a solução mais elegante e você não veja nenhuma
if
, mas a biblioteca padrão realmente cobre esse caso :, vejastd::call_once
.A vantagem aqui é que isso é seguro para threads.
fonte
call_once
Me ver significa que você quer ligar uma vez. Louco, eu sei.O C ++ possui uma primitiva de fluxo de controle integrada que consiste em "( antes do bloco; condição; pós-bloco )" já:
Ou mais hackier, mas mais curto:
No entanto, acho que qualquer uma das técnicas apresentadas aqui deve ser usada com cuidado, pois não são (ainda?) Muito comuns.
fonte
b == false
:Thread 1
avalia!b
e entra no loop,Thread 2
avalia!b
e entra no loop,Thread 1
faz suas coisas e deixa o loop for, configurandob == false
como!b
ieb = true
...Thread 2
faz seu material e as folhas do loop for, definindob == true
a!b
ieb = false
, permitindo que todo o processo deve ser repetido indefinidamente)b=!b
, isso parece legal, mas você realmente deseja que o valor seja falso, portantob=false
deve ser preferido.No C ++ 17 você pode escrever
para evitar brincar com
i
o corpo do laço.i
começa com 0 (garantido pelo padrão) e a expressão após os;
conjuntosi
para1
a primeira vez que é avaliada.Observe que no C ++ 11 você pode conseguir o mesmo com uma função lambda
o que também traz uma pequena vantagem, pois
i
não vaza para o corpo do loop.fonte
static int i;
possa (não tenho certeza) um desses casos em quei
é garantido que seja inicializado0
, é muito mais claro usarstatic int i = 0;
aqui.Esta solução é segura para threads (ao contrário de muitas das outras sugestões).
fonte
()
é opcional (se vazio) na declaração lambda?Você pode agrupar a ação única no construtor de um objeto estático que você instancia no lugar da condicional.
Exemplo:
Ou você pode realmente ficar com uma macro, que pode ser algo como isto:
fonte
Como o @damon disse, você pode evitar o uso
std::exchange
usando um número inteiro decrescente, mas é preciso lembrar que os valores negativos são verdadeiros. A maneira de usar isso seria:Traduzir isso para o invólucro sofisticado da @ Acorn ficaria assim:
fonte
Embora o uso
std::exchange
sugerido pelo @Acorn seja provavelmente a maneira mais idiomática, uma operação de troca não é necessariamente barata. Embora seja claro que a inicialização estática é garantida como segura para threads (a menos que você diga ao seu compilador para não fazê-lo), qualquer consideração sobre desempenho é algo fútil de qualquer maneira na presença dastatic
palavra - chave.Se você está preocupado com a micro-otimização (como costumam usar as pessoas que usam C ++), também pode
bool
usar e fazer scratchint
, o que permitirá que você use o pós-decremento (ou melhor, o incremento , pois, ao contrário dobool
decrementar de umint
, não saturará zero) ...):Costumava ter
bool
operadores de incremento / decremento, mas eles foram preteridos há muito tempo (C ++ 11? Não tenho certeza?) E devem ser totalmente removidos no C ++ 17. No entanto, você pode diminuir umaint
multa e, é claro, funcionará como uma condição booleana.Bônus: Você pode implementar
do_twice
oudo_thrice
similarmente ...fonte
bool
e diminuir uma vez. Mas o incremento funciona bemint
. Veja a demonstração on-line: coliru.stacked-crooked.com/a/ee83f677a9c0c85ado_once
envolve e eventualmente atinge 0 novamente (e novamente e novamente ...).Com base na ótima resposta da @ Bathsheba para isso - simplificou ainda mais.
Em
C++ 17
, você pode simplesmente fazer:(Nas versões anteriores, basta declarar
int i
fora do bloco. Também funciona em C :)).Em palavras simples: você declara i, que assume o valor padrão de zero (
0
). Zero é falsey, portanto, usamos o!
operador de ponto de exclamação ( ) para negá-lo. Em seguida, levamos em conta a propriedade de incremento do<ID>++
operador, que primeiro é processada (atribuída etc.) e depois incrementada.Portanto, neste bloco, eu serei inicializado e só terá o valor
0
uma vez, quando o bloco for executado, e o valor aumentará. Simplesmente usamos o!
operador para negá-lo.fonte