Sejak C ++ 17 seseorang dapat menulis if
blok yang akan dieksekusi persis seperti ini:
#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;
}
}
}
Saya tahu saya mungkin terlalu memikirkan ini, dan ada cara lain untuk menyelesaikannya, tetapi tetap - apakah mungkin untuk menulis ini dengan cara seperti ini, jadi tidak perlu do_once = false
bagian akhir?
if (DO_ONCE) {
// Do stuff
}
Saya sedang memikirkan fungsi pembantu do_once()
,, berisi static bool do_once
, tetapi bagaimana jika saya ingin menggunakan fungsi yang sama di tempat yang berbeda? Mungkinkah ini waktu dan tempat untuk #define
? Saya harap tidak.
c++
if-statement
c++17
nada
sumber
sumber
if (i == 0)
? Cukup jelas.std::call_once
adalah opsi (ini digunakan untuk threading, tetapi masih berfungsi).if
kondisi bisa jadistatic
. Itu pintar.Jawaban:
Penggunaan
std::exchange
:if (static bool do_once = true; std::exchange(do_once, false))
Anda dapat mempersingkatnya dengan membalikkan nilai kebenaran:
if (static bool do_once; !std::exchange(do_once, true))
Tetapi jika Anda sering menggunakan ini, jangan berlebihan dan buatlah pembungkus sebagai gantinya:
struct Once { bool b = true; explicit operator bool() { return std::exchange(b, false); } };
Dan gunakan seperti:
if (static Once once; once)
Variabel tidak seharusnya direferensikan di luar kondisi tersebut, sehingga nama tidak banyak membeli. Mengambil inspirasi dari bahasa lain seperti Python yang memberikan arti khusus pada
_
pengenalnya, kami dapat menulis:if (static Once _; _)
Perbaikan lebih lanjut: manfaatkan bagian BSS (@Deduplicator), hindari penulisan memori ketika kita sudah menjalankan (@ShadowRanger), dan berikan petunjuk prediksi cabang jika Anda akan menguji berkali-kali (misalnya seperti dalam pertanyaan):
// GCC, Clang, icc only; use [[likely]] in C++20 instead #define likely(x) __builtin_expect(!!(x), 1) struct Once { bool b = false; explicit operator bool() { if (likely(b)) return false; b = true; return true; } };
sumber
#define ONLY_ONCE if (static bool DO_ONCE_ = true; std::exchange(DO_ONCE_, false))
Untuk digunakan sebagai:ONLY_ONCE { foo(); }
_
ini digunakan di banyak perangkat lunak untuk menandai string yang dapat diterjemahkan. Harapkan hal-hal menarik terjadi._
untuk variabel akan ada un-Pythonic. Anda tidak menggunakan_
variabel yang akan direferensikan nanti, hanya untuk menyimpan nilai di mana Anda harus memberikan variabel tetapi Anda tidak memerlukan nilainya. Ini biasanya digunakan untuk membongkar ketika Anda hanya membutuhkan beberapa nilainya. (Ada kasus penggunaan lain, tetapi sangat berbeda dari kasus nilai sekali pakai.)Mungkin bukan solusi yang paling elegan dan Anda tidak melihat yang sebenarnya
if
, tetapi pustaka standar sebenarnya mencakup kasus ini :, lihatstd::call_once
.#include <mutex> std::once_flag flag; for (int i = 0; i < 10; ++i) std::call_once(flag, [](){ std::puts("once\n"); });
Keuntungannya di sini adalah benang ini aman.
sumber
call_once
melihatku berarti kamu ingin menelepon sesuatu sekali. Gila, saya tahu.C ++ memang memiliki primitif aliran kontrol bawaan yang terdiri dari "( before-block; condition; after-block )" sudah:
for (static bool b = true; b; b = false)
Atau lebih hack, tapi lebih pendek:
for (static bool b; !b; b = !b)
Namun, menurut saya teknik apa pun yang disajikan di sini harus digunakan dengan hati-hati, karena tidak (belum?) Sangat umum.
sumber
b == false
:Thread 1
mengevaluasi!b
dan masuk untuk loop,Thread 2
mengevaluasi!b
dan masuk untuk loop,Thread 1
melakukan tugasnya dan meninggalkan loop for, mengaturb == false
ke!b
ieb = true
...Thread 2
melakukan tugasnya dan meninggalkan loop for, menyetelb == true
ke!b
ieb = false
, memungkinkan seluruh proses diulang tanpa batas)b=!b
, itu terlihat bagus, tetapi Anda sebenarnya ingin nilainya salah, jadib=false
lebih disukai.Di C ++ 17 Anda bisa menulis
if (static int i; i == 0 && (i = 1)){
untuk menghindari bermain-main dengan
i
di tubuh lingkaran.i
dimulai dengan 0 (dijamin oleh standar), dan ekspresi setelah;
diseteli
ke1
pertama kali dievaluasi.Perhatikan bahwa di C ++ 11 Anda bisa mencapai hal yang sama dengan fungsi lambda
if ([]{static int i; return i == 0 && (i = 1);}()){
yang juga membawa sedikit keuntungan karena
i
tidak bocor ke loop body.sumber
static int i;
mungkin (saya benar-benar tidak yakin) menjadi salah satu kasus di manai
dijamin akan diinisialisasi0
, jauh lebih jelas untuk digunakan distatic int i = 0;
sini.static bool once = [] { std::cout << "Hello one-shot\n"; return false; }();
Solusi ini aman untuk utas (tidak seperti banyak saran lainnya).
sumber
()
opsional (jika kosong) pada deklarasi lambda?Anda bisa menggabungkan tindakan satu kali dalam konstruktor objek statis yang Anda buat sebagai pengganti kondisional.
Contoh:
#include <iostream> #include <functional> struct do_once { do_once(std::function<void(void)> fun) { fun(); } }; int main() { for (int i = 0; i < 3; ++i) { static do_once action([](){ std::cout << "once\n"; }); std::cout << "Hello World\n"; } }
Atau Anda mungkin tetap menggunakan makro, yang mungkin terlihat seperti ini:
#include <iostream> #define DO_ONCE(exp) \ do { \ static bool used_before = false; \ if (used_before) break; \ used_before = true; \ { exp; } \ } while(0) int main() { for (int i = 0; i < 3; ++i) { DO_ONCE(std::cout << "once\n"); std::cout << "Hello World\n"; } }
sumber
Seperti yang dikatakan @damon, Anda dapat menghindari penggunaan
std::exchange
dengan menggunakan decrementing integer, tetapi Anda harus ingat bahwa nilai negatif berubah menjadi true. Cara menggunakannya adalah:if (static int n_times = 3; n_times && n_times--) { std::cout << "Hello world x3" << std::endl; }
Menerjemahkan ini ke bungkus mewah @ Acorn akan terlihat seperti ini:
struct n_times { int n; n_times(int number) { n = number; }; explicit operator bool() { return n && n--; }; }; ... if(static n_times _(2); _) { std::cout << "Hello world twice" << std::endl; }
sumber
Meskipun menggunakan
std::exchange
seperti yang disarankan oleh @Acorn mungkin adalah cara yang paling idiomatis, operasi pertukaran tidak selalu murah. Meskipun tentu saja inisialisasi statis dijamin aman untuk thread (kecuali jika Anda memberi tahu kompiler Anda untuk tidak melakukannya), jadi pertimbangan apa pun tentang kinerja agak sia-sia dengan adanyastatic
kata kunci.Jika Anda khawatir tentang pengoptimalan mikro (seperti yang sering dilakukan orang yang menggunakan C ++), Anda dapat menggaruk
bool
dan menggunakanint
sebagai gantinya, yang akan memungkinkan Anda untuk menggunakan penurunan pasca (atau lebih tepatnya, kenaikan , tidak sepertibool
penurunan yang tidakint
akan jenuh ke nol ...):if(static int do_once = 0; !do_once++)
Dulu
bool
operator tersebut memiliki operator increment / decrement, tetapi sudah lama tidak digunakan lagi (C ++ 11? Tidak yakin?) Dan harus dihapus seluruhnya dalam C ++ 17. Namun demikian, Anda dapat menurunkan fileint
baik, dan itu tentu saja akan bekerja sebagai kondisi Boolean.Bonus: Anda dapat menerapkan
do_twice
ataudo_thrice
serupa ...sumber
bool
dan mengurangi sekali waktu. Tapi kenaikan berfungsi dengan baikint
. Lihat demo online: coliru.stacked-crooked.com/a/ee83f677a9c0c85ado_once
membungkus dan akhirnya akan mencapai 0 lagi (dan lagi dan lagi ...).Berdasarkan jawaban bagus @ Batsyeba untuk ini - hanya membuatnya lebih sederhana.
Di
C++ 17
, Anda cukup melakukan:if (static int i; !i++) { cout << "Execute once"; }
(Di versi sebelumnya, cukup nyatakan di
int i
luar blok. Juga berfungsi di C :)).Dengan kata sederhana: Anda mendeklarasikan i, yang mengambil nilai default nol (
0
). Nol adalah falsey, oleh karena itu kami menggunakan!
operator tanda seru ( ) untuk meniadakannya. Kami kemudian memperhitungkan properti increment dari<ID>++
operator, yang pertama diproses (ditugaskan, dll) dan kemudian bertambah.Oleh karena itu, di blok ini, saya akan diinisialisasi dan memiliki nilai
0
hanya satu kali, ketika blok dieksekusi, maka nilainya akan meningkat. Kami hanya menggunakan!
operator untuk meniadakannya.sumber