5.2.30 · D3 · Coding › C++ Programming › noexcept specifier
Intuition Ek keyword ke liye "scenario matrix" kyun?
noexcept mein koi numbers nahi hain, lekin iske paas ek fixed set of case classes zaroor hain: do roles (specifier vs operator), do truth values (true/false), escape-vs-caught boundary, std::vector mein reallocation branch, aur degenerate corners (destructors, deleted function se throw(), empty expressions). Agar hum har cell ko cover kar lein, toh koi bhi noexcept situation aisi nahi hogi jo tumne pehle se worked out na dekhi ho. Yeh page ek exhaustive tour hai.
Yeh page noexcept specifier ki child hai. Agar neeche koi term unfamiliar lage, toh parent page pehle usse build karta hai.
Har question jo noexcept poochh sakta hai, woh in cells mein se kisi ek mein aata hai. Har worked example ko us cell ke saath tag kiya gaya hai jo woh cover karta hai.
#
Case class
Concrete question
Expected outcome
A
Operator on a promised function
noexcept(safe()) jahan safe noexcept hai
true
B
Operator on a default function
noexcept(risky()) jahan risky throw kar sakta hai
false
C
Escape (contract broken)
throw noexcept function se bahar nikalta hai
std::terminate()
D
Caught inside (contract kept)
throw noexcept function ke andar hi catch ho jaata hai
theek hai, koi terminate nahi
E
Conditional true branch
ek template jiska move throw nahi kar sakta
noexcept(...) → true
F
Conditional false branch
ek template jiska move throw kar sakta hai
noexcept(...) → false
G
std::vector reallocation
move-noexcept type ke vector ko grow karna vs nahi
move vs copy
H
Degenerate: destructor
~S par implicit noexcept
throwing → terminate
I
Degenerate: empty / non-throwing ops
noexcept(1 + 2), plain reads par noexcept(x)
true
J
Exam twist: nested operator
noexcept(noexcept(x)) — kaun sa specifier hai?
outer = specifier
Rows A/B operator ke do truth values hain; C/D escape boundary ke do sides hain; E/F ek conditional ki do branches hain; G payoff hai; H/I/J degenerate aur trick corners hain. noexcept ke baare mein kuch bhi is table ke bahar nahi hai.
Worked example Ek promised aur ek default function ko query karo
int safe () noexcept { return 42 ; } // promise: kabhi throw nahi karega
int risky () { throw 1 ; } // koi promise nahi (default may-throw)
constexpr bool s = noexcept ( safe ()); // ?
constexpr bool r = noexcept ( risky ()); // ?
Forecast: aage padhne se pehle s aur r guess karo.
safe ki declaration dekho. Uspar noexcept specifier hai.
Yeh step kyun? Operator noexcept(x) x ko run nahi karta — woh declared promise ko read karta hai . safe ne promise kiya hai koi throw nahi, toh operator true answer karta hai. → s == true.
risky ki declaration dekho. Koi specifier nahi, toh by default yeh noexcept(false) hai.
Yeh step kyun? Operator ko kisi bhi aise cheez ke liye worst assume karna padta hai jo non-throwing promise nahi karti. Andar ka throw 1 irrelevant hai — ek empty default function bhi false deta hai, kyunki operator declarations par trust karta hai, bodies par nahi. → r == false.
Verify: s true hai, r false hai. Kyunki dono constexpr hain, compiler isse compile time par prove karta hai — tum static_assert(s && !r); likh sakte ho aur woh hold karega.
Cell A ne true diya, Cell B ne false diya. Dono truth values ab exist karte hain.
Worked example Promise todo
void liar () noexcept { throw std :: runtime_error ( "oops" ); }
int main () {
try { liar (); }
catch (...) { /* kya yeh run hoga? */ }
}
Forecast: kya catch isse catch karega?
liar ne noexcept promise kiya tha. Compiler ne, is promise par trust karke, liar ke liye koi unwinding tables emit nahi kiye (dekho Exception handling in C++ ).
Yeh step kyun? Promise ka poora cost saving wahi machinery skip karne mein hai. Ab stack walk karne ke liye kuch bhi nahi hai.
throw fire hota hai aur liar se bahar nikalne ki koshish karta hai. Yeh function boundary tak pahunchta hai — yahi escape ka moment hai.
Yeh step kyun? Trigger throw khud nahi hai, balki escape hai. std::terminate dekho: runtime boundary crossing ko watch karta hai.
Runtime std::terminate() call karta hai. Program terminate ho jaata hai.
Yeh step kyun? Unwinding info ke bina, rethrowing impossible hai; ek hi defined action hai — ruk jaao. main mein catch (...) kabhi run nahi hota .
Verify (mental sanity check): agar catch run hua hota toh hum promise-break se recover kar lete — jo promise ko meaningless bana deta. Terminate hi ek self-consistent outcome hai.
Worked example Throw karo aur swallow karo, saara function ke andar
void tidy () noexcept {
try {
throw 1 ; // throw hua...
} catch ( int ) {
// ...yahan pe hi catch hua. Kuch bhi escape nahi kiya.
}
}
int main () { tidy (); } // normally return karta hai
Forecast: terminate hoga ya nahi?
Identify karo ki contract kya forbid karta hai. noexcept specifier re-read karo: yeh ek exception ko escape karne se rokta hai, sirf throw ke exist karne se nahi.
Yeh step kyun? Log "has a throw" aur "throws out" ko confuse karte hain. Contract mein word escape hai.
Exception ki path trace karo. throw 1 → same function mein catch (int) se immediately match → wahan destroy.
Yeh step kyun? Kyunki yeh kabhi function boundary tak nahi pahuncha, Example 2 ka step 2 kabhi hota hi nahi.
Function normally return karta hai. Koi terminate nahi.
Verify: boundary kabhi ek live exception se cross nahi hui, toh promise ("kuch nahi escape karega") kept hai. Example 2 se compare karo jahan throw boundary tak pahuncha — same keyword, opposite fate, purely escape vs caught se decide.
noexcept type se compute hota hai
struct Fast { Fast (Fast && ) noexcept ; }; // move promises no throw
struct Slow { Slow (Slow && ); }; // move may throw
template < class T >
void relocate ( T & x ) noexcept ( noexcept ( T ( std :: move (x))) ) { /* ... */ }
Forecast: kya relocate<Fast> noexcept hai? Kya relocate<Slow> noexcept hai?
Do noexcepts ko alag karo. Outer noexcept( ... ) () ke baad right mein = specifier (promise). Inner noexcept( T(std::move(x)) ) = operator (ek compile-time bool).
Yeh step kyun? Yeh Move semantics idiom hai: "main exactly tab promise karta hoon jab T ko move karna throw nahi kar sakta." Roles straight karna Cell J ka poora point hai, yahan preview hai.
Fast ke saath instantiate karo. Inner operator poochta hai: kya Fast(std::move(x)) non-throwing hai? Fast ka move ctor noexcept declare hai → inner true hai → specifier noexcept(true) hai. Cell E.
Slow ke saath instantiate karo. Inner operator wahi Slow se poochta hai. Uske move ctor ka koi promise nahi → inner false hai → specifier noexcept(false) hai. Cell F.
Verify: noexcept(relocate<Fast>(f)) == true aur noexcept(relocate<Slow>(s)) == false. Ek hi template ne dono truth values produce kiye, purely type se driven — exactly woh conditional jo parent ne promise kiya tha.
Worked example Ek vector grow karna — move ya copy?
Ek std::vector jo 4 elements hold kar raha hai, full hai aur grow karna chahta hai. Woh ek bada buffer allocate karta hai aur 4 elements ko transfer karna padta hai.
Forecast: vector<Fast> vs vector<Slow> ke liye, kya elements move honge ya copy ?
Danger state karo. 4 mein se element 3 transfer karte waqt throw ho sakta hai. Agar hum move kar rahe the, toh elements 0–2 purane buffer mein already gutted hain aur element 3 naye mein half-built hai — corrupted state, Strong exception guarantee khatam.
Yeh step kyun? Strong guarantee kehti hai: agar operation throw kare, toh container unchanged rahe. Figure mein red buffer dekho — ek thrown move usse unrecoverable chhod deta hai.
vector<Fast>: moves noexcept hain. Woh throw nahi kar sakte , toh corruption scenario impossible hai. vector std::move_if_noexcept use karta hai, jo rvalue return karta hai → move (green fast path).
vector<Slow>: moves throw kar sakte hain. Safe rehne ke liye, move_if_noexcept ek lvalue return karta hai → copy . Agar copy throw kare, toh purane buffer mein originals untouched hain, toh container unchanged hai (blue safe path).
Verify (kaam count karo): N = 4 elements ke saath, vector<Fast> 4 cheap moves karta hai; vector<Slow> 4 full copies karta hai aur success tak purana buffer alive rakhta hai. Same result, different cost — ek keyword se decide move constructor par.
Worked example Ek throwing destructor
struct S {
~S () { throw std :: runtime_error ( "bad" ); } // explicit noexcept nahi likha
};
int main () { S s; } // s scope ke end par destroy hota hai
Forecast: kya missing noexcept ka matlab "may throw" hai, toh yeh theek hai?
Implicit rule yaad karo. C++11 se, Destructors implicitly noexcept hote hain chahe tum kuch nahi likho.
Yeh step kyun? Toh ~S() aise behave karta hai jaise tumne ~S() noexcept likha ho. Keyword ka absent hona isse may-throw nahi banata (unlike Cell B mein ordinary functions).
Destructor throw karta hai → escape. Yeh Cell C disguise mein hai: ek noexcept function se escape.
Yeh step kyun? Escape rule parwah nahi karta kaise function ko uska promise mila — implicit ya explicit, escape = terminate.
std::terminate() fire hota hai.
Verify: language throwing destructors ko exactly isliye ban karti hai kyunki destructor kisi doosri exception ki unwinding ke dauran run ho sakta hai — ek saath do live exceptions undefined chaos hai. ~S ko default noexcept banana us chaos ko ek clean, immediate terminate mein convert karta hai.
Worked example Pure computations aur plain reads
int x = 5 ;
constexpr bool a = noexcept ( 1 + 2 ); // ?
constexpr bool b = noexcept (x); // ?
constexpr bool c = noexcept (x * x - 3 ); // ?
Forecast: teeno guess karo.
Har operation mein throw path check karo. Built-in int arithmetic aur variable read karne mein koi throwing operations nahi hain — koi allocation nahi, koi user code nahi, koi function call nahi jo throw kar sake.
Yeh step kyun? Operator ka rule (parent [!formula]): true tab jab expression ki har operation known non-throwing ho.
Teeno expressions pure built-ins hain. Toh har operator true yield karta hai. → a == b == c == true.
Yeh step kyun? Yeh degenerate baseline hain: jab throw karne wali koi cheez nahi, answer trivially true hai. Isliye leaf function par noexcept aksar justified hota hai.
Verify: a && b && c true hai. Integer overflow bhi (jo UB hai, exception nahi) operator ko false nahi banata — operator sirf throwing track karta hai, saari misbehaviour nahi. Parent ki mistake se tie back karta hai: "noexcept ≠ safe."
Worked example Kaun sa noexcept kaun sa hai?
template < class T >
void ping ( T && t ) noexcept ( noexcept ( f ( std :: forward < T >(t)) ) );
Forecast: noexcept( noexcept(...) ) mein, kaun sa promise hai aur kaun sa bool?
Outer noexcept ki position locate karo. Woh parameter list (T&& t) ke immediately baad baitha hai.
Yeh step kyun? Position role decide karti hai. Ek noexcept(...) specifier slot mein (signature ke baad) hamesha specifier hota hai — promise.
Inner noexcept padho. Woh outer ke parentheses ke andar hai, toh woh ek operator hai jo bool produce karta hai: "kya f(std::forward<T>(t)) non-throwing hai?"
Yeh step kyun? Operator kahin bhi appear ho sakta hai jahan bool chahiye; yahan woh specifier ko feed karta hai.
Combine karo. "ping no-throw promise karta hai exactly tab jab forwarded argument par f call karna throw nahi kar sakta." Outer = specifier, inner = operator.
Verify: ek concrete f substitute karo. Agar f noexcept declare hai, inner → true, toh ping noexcept(true) hai. Agar f throw kar sakta hai, inner → false, toh ping noexcept(false) hai aur throw normally propagate karta hai (koi terminate nahi — kyunki promise honestly false tha). Dono branches self-consistent.
Recall Kya har cell hit hua?
Kaun se examples do operator truth values cover karte hain? ::: Example 1 (Cells A true, B false).
Kaun se do examples escape-vs-caught pair hain? ::: Example 2 (Cell C, escape → terminate) aur Example 3 (Cell D, caught → fine).
Kaun sa example dono conditional-noexcept branches produce karta hai? ::: Example 4 (Cells E aur F).
Kaun sa example vector move-vs-copy payoff hai? ::: Example 5 (Cell G).
Kaun se examples degenerate corners hain? ::: Example 6 (Cell H, destructor), Example 7 (Cell I, pure expressions).
Kaun sa example specifier-vs-operator exam twist hai? ::: Example 8 (Cell J).
Mnemonic Poora page ek line mein
"Ask, Escape, Choose." Operator ask karta hai (A/B/E/F/I/J), boundary escape decide karti hai (C/D/H), aur vector move ya copy choose karta hai (G).
Kya decide karta hai ki noexcept function mein throw terminate call karega? Kya woh function se escape karta hai; andar caught hona theek hai, boundary cross karna fatal hai.
vector<Fast> (move noexcept) vs vector<Slow> (move may throw) ke liye, reallocation kya karta hai?Fast elements ko move karta hai (fast path), Slow elements ko copy karta hai (safe path) strong guarantee preserve karne ke liye.
noexcept(noexcept(x)) mein, kaun sa specifier hai?Outer wala (woh signature ke baad specifier slot mein baitha hai); inner operator hai.
Kya noexcept(1 + 2) true hai ya false? true — built-in arithmetic mein koi throwing operation nahi hai.
Kya destructors jo noexcept omit karte hain, may-throw hote hain? Nahi — C++11 se woh implicitly noexcept hote hain; unse throw karna terminate call karta hai.