Authors: Matt Cummins, Valentyn Yukhymenko
Document: P3816R2
Date: 2026-01-13
Target: SG7, LEWG, EWG
Link: wg21.link/p3816r2
P2996 gave us meta::info for reflection. P3372 made unordered containers constexpr. P3816 fills the gap between them: you still can't use meta::info as a key in an unordered_map because there's no hash for it.
This paper proposes consteval_hash<meta::info> - a new class template specifically for compile-time hashing, separate from std::hash. The argument is that specializing std::hash doesn't work cleanly because hash has runtime requirements inconsistent with consteval-only types. So instead we get a parallel hash type that lives entirely at compile time.
The design settles on "semi-stable" semantics - hash values are stable across repeated compilations of the same source but may change if the source changes. There's a discussion of ABI implications and a table of twelve alternative names they considered and rejected. R2 tidies up the motivation section and adds an annotations example.
Nice to see the reflection plumbing papers keep landing. The
mp_uniqueexample is exactly the kind of thing you want to be able to write without gymnastics.The interesting design choice here is the semi-stable hash. From the paper:
This is a meaningful middle ground. An unstable hash (just hash the AST pointer) is trivial to implement but kills reproducible builds. A fully stable hash requires the compiler to derive a value from the semantic identity of the reflection - name, scope, parameter types - which is expensive and fragile as the language adds new reflectable constructs.
Semi-stable sidesteps both by interning: assign each AST node a deterministic integer ID from a counter as you encounter it. Same source, same traversal order, same IDs, same hashes. Change the source, the traversal changes, IDs shift. It's the approach from their second clang-p2996 PR and it's clever.
The question I'd have for LEWG is whether users will accidentally depend on cross-TU stability that isn't guaranteed. The paper's ABI section has a somewhat contrived example with buffer sizes, but the real risk is someone stashing hash values into a constexpr variable in a header and expecting the same value in every TU that includes it.
Honest question - who is stashing compile-time hash values into headers? The intended use case is keying an
unordered_mapinside a consteval function. The hash never escapes the constant evaluator. The ABI scenario in the paper requires you to go out of your way to do something weird with the value.Table of twelve rejected names is the real content of this paper. I lost it at:
consteval_hashis fine. Move on, LEWG.But
meta::infois already consteval-only. There are no runtime requirements to conflict with. You could specializehash<meta::info>as consteval and it would be perfectly consistent - you can't usemeta::infoat runtime anyway.The argument for a separate type only holds if you plan to extend
consteval_hashto types that do have runtime hash specializations (int,string, etc.). Section 9 confirms that's the plan. So this is really a paper about creating a parallel compile-time hash framework, not about hashingmeta::info. The title undersells it.Scope note: Section 9.1 proposes extending
consteval_hashto all arithmetic, enum, and pointer types. Section 9.2 proposes adding a template parameter for randomized vs stable hashing. That's a significant expansion from "hashmeta::infofor unordered containers."I'd expect LEWG to ask them to split - ship the
meta::infospecialization now, iterate on the broader framework in a follow-up. That's how P2996 itself progressed: narrow scope first, extensions later.Every time I see a paper that depends on P2996 and P3372 I have to check - are those actually in C++26 yet? checks okay yes they are. We're building on top of things that exist now. Progress.