P3822R1 - Conditional noexcept specifiers in compound requirements WG21
Posted by u/standards_rss_addict · 5 hr. ago

Authors: Viacheslav Luchkin, Gašper Ažman
Document: P3822R1
Date: 2026-02-14
Target: EWG → CWG
Link: wg21.link/p3822r1

You know how function declarations have had noexcept(expr) since C++11? Turns out you can't do that in a compound requirement. You get bare noexcept or nothing. So if you need to conditionally check whether an expression is non-throwing - say, in a type-erased wrapper parameterized on noexcept - you have to duplicate the entire concept body with a ternary: one branch that checks noexcept, one that doesn't.

P3822 extends compound requirements to accept noexcept(constant-expression), filling the gap. Small change, reuses existing grammar from [except.spec], has a Clang implementation. EWG forwarded it to CWG for C++29.

▲ 47 points (89% upvoted) · 8 comments
sorted by: best
u/turbo_llama_9000 27 points 3 hr. ago

love that we're three decades into C++ and still finding grammar cells that noexcept forgot to colonize

u/yet_another_cpp_dev 19 points 4 hr. ago

Wait - noexcept(expr) has been legal on function declarations since C++11 but not in compound requirements? I genuinely assumed this already worked. Who discovered this gap?

u/compiles_first_try 6 points 2 hr. ago

Anyone writing type-erased wrappers parameterized on noexcept. The workaround is a ternary concept that duplicates the entire body:

template<typename F, bool noexc>
concept invocable = noexc
  ? requires(F f) { { f() } noexcept; }
  : requires(F f) { f(); };

It works but it's the kind of thing that makes you wonder if the language is trolling you. The paper's type erasure example on Godbolt is the more complete motivating case.

u/concept_enjoyer 12 points 1 hr. ago

The lack of support for this syntax is also inconsistent with function declarations, which have had conditional noexcept specifiers since C++11.

Small paper but the inconsistency it fixes is real. The proposed wording reuses the existing noexcept-specifier grammar from [except.spec] verbatim - no new production rules, just plumbing the existing one into compound requirements where it should have been all along.

EWG forwarded to CWG for C++29: SF 10, F 16, N 4, A 4, SA 0. Four against is a little surprising for something this mechanical. Maybe concern about the grammar surface area? Or just people who don't think the ternary workaround is painful enough to justify a language change.

There's a Clang fork with a working implementation. Apparently most of the existing exception specification logic was reusable, which is kind of the point.

u/sfinae_survivor 4 points 47 minutes ago

One design detail worth noting: if the constant-expression fails to convert to bool, the requirement is not satisfied rather than a hard error. Same reasoning as SFINAE - you want overload resolution to keep looking, not detonate.

Given that noexcept(true) and noexcept(false) are the only realistic values, it's hard to see how you'd actually trigger this in practice. But it's a nice safety net for heavily-templated constraint machinery where someone substitutes a type that doesn't even have a viable conversion.

u/volatile_as_intended 8 points 2 hr. ago

I've hit exactly this writing a move_only_function alternative at work. Ended up with two separate concepts and a requires clause that looked like it was generated by a macro. Would genuinely love to just write noexcept(noexc) in the compound requirement and be done with it.

u/not_a_template_wizard 3 points 53 minutes ago

Honest question: does anyone actually get noexcept right in generic code? Every time I try I end up in a noexcept(noexcept(noexcept(...))) hall of mirrors and give up.

u/yet_another_cpp_dev 2 points 41 minutes ago

At scale, yes. Anything that does type erasure or wraps callables needs to propagate it. std::function doesn't bother but std::move_only_function does, and that distinction matters in performance-sensitive code.