C++26: साधारण अनंत लूप अब undefined behavior नहीं रहेंगे
C++26 में भाषा “trivial” अनंत लूप जैसे `while (true);` को संभालने का तरीका बदलती है, जो पहले undefined behavior थे और जिन्हें हटाया जा सकता था, कभी-कभी नियंत्रण प्रवाह को असंबंधित कोड में भी ले जाते हुए। टिप्पणीकार बहस करते हैं कि क्या यह पुराना नियम कभी समझ में आता था, और embedded तथा kernel code में इसके वास्तविक उपयोगों की ओर इशारा करते हैं, जहाँ ऐसे लूप error पर halt करने या interrupts का इंतज़ार करने के लिए उपयोग होते हैं। नए मानक में इन लूप्स को `std::this_thread::yield()` कॉल की तरह मॉडल करना कुछ लोगों को “forward progress” आवश्यकताओं के लिए व्यावहारिक लगता है, जबकि दूसरों को यह hidden system calls और जटिलता का अनावश्यक स्रोत लगता है, खासकर ऐसे systems programming language में जो पहले से ही undefined behavior से जूझ रहा है।
परिवर्तन का दायरा और चौंकाने वाला व्यवहार
- कई टिप्पणीकार इस बात से हैरान हैं कि साधारण अनंत लूप “लगभग 1/6 शताब्दी” तक UB थे, जिससे ऐसी चीजें हो सकती थीं जैसे:
- कंपाइलर जब लूप और एपिलॉग हटा देता है, तो फ़ंक्शन का “अगले फ़ंक्शनों में गिर जाना।”
returnके बिना non-void फ़ंक्शनों का बस आगे दूसरे कोड में जारी रहना।
- कुछ लोग इसे सहज debugging अपेक्षाओं का बड़ा उल्लंघन मानते हैं (“समस्या हटने तक कोड हटाओ” तब काम नहीं करता, अगर एक न्यूनतम लूप कुछ भी कर सकता है)।
अनंत लूप UB क्यों थे (optimization / forward progress)
- कई पोस्ट इसे “forward progress guarantees” से जोड़ती हैं: कंपाइलर मान सकते हैं कि थ्रेड्स अंततः प्रगति करेंगे (return, I/O, volatile, atomics)।
- अनंत लूप पर UB ने यह संभव बनाया:
- termination साबित किए बिना लूप merging या reordering।
- pure computations के ऊपर stores hoist करने जैसे transformations।
- कुछ लोगों को यह भरोसा नहीं है कि ये optimizations semantic अजीबपन के लायक हैं; उनके अनुसार यह ज़्यादा benchmark contests की बात है, वास्तविक उपयोगकर्ता लाभ की नहीं।
Embedded और systems के use cases
- “trivial infinite loops के लिए कभी कोई अच्छा कारण नहीं होता” — इस पर काफ़ी असहमति है।
- Embedded और bare-metal developers कहते हैं:
- error पर halt करने वाले handlers।
- interrupt-driven systems पर main “super loops।”
- महंगे sleep/wake transitions की बजाय जागे रहना।
- दूसरे तर्क देते हैं कि आपको dedicated CPU halt/sleep instructions या error-indicating loops (जैसे blink codes) इस्तेमाल करने चाहिए, खाली spins नहीं।
नई C++26 behavior (std::this_thread::yield)
- नया नियम बहुत ही संकीर्ण केस पर लागू होता है: constant condition वाला trivially empty infinite loop।
- आलोचना इन बातों पर केंद्रित है:
- इसे एक implicit
this_thread::yield()call से बदलना, यानी ऐसा कोड जो side-effect-free दिखता है लेकिन उसमें एक invisible system call हो। - freestanding / embedded / sandboxed environments या real-time settings में जोखिम।
- असंगति:
while(true);बनामwhile(true) continue;बनाम अन्य रूपों के अलग semantics।
- इसे एक implicit
- कुछ लोग इसे UB से बेहतर मानते हैं, लेकिन फिर भी इसे “भयानक” कहते हैं और इसके बजाय यह देखना चाहेंगे:
- एक diagnostic/warning।
- standard library का “halt here” primitive।
- या C11-शैली नियम (constant-condition infinite loops बस non-terminating माने जाएँ)।
UB और C++ की दिशा को लेकर व्यापक चिंताएँ
- UB को optimization के tool और miscompilations / अबोधगम्य व्यवहार के स्रोत के रूप में लेकर लंबी चर्चा हुई।
- C और Rust से तुलना:
- C में constant-expression loops का special-casing ज़्यादा sane माना गया।
- Rust का
loop {}और LLVM का इतिहास इस मुद्दे को ठीक करने के पहले के दबाव के रूप में उल्लेखित हैं।
- कई टिप्पणियाँ आधुनिक C++ में बढ़ते hidden behavior और complexity पर अफसोस जताती हैं, और डर व्यक्त करती हैं कि freestanding / embedded use और कठिन होता जा रहा है।