Five small C++29 papers from Brno you will actually use
The Brno meeting was mostly about safety, but underneath the headline work the committee cleared a stack of small, practical papers. None of them will make a conference keynote. All of them remove a daily papercut, and all target C++29.
Three of these are concrete enough to run today, in the only sense currently possible: no released compiler implements the C++29 syntax yet (June 2026), so the runnable examples below show the workaround each paper deletes, with the future one-liner sitting in a comment. Compile both and you can see exactly how much boilerplate goes away.
Designated-initializers that reach through a base class
C++20 gave us designated initializers but forbade them for any aggregate with a base class. P2287, “Designated-initializers for Base Classes” (Revzin) lifts that. There is no special base-class designator. You just name the inherited member directly:
struct A { int a; };
struct B : A { int b; };
auto b1 = B{.a = 1, .b = 2}; // name the inherited member directly
auto b2 = B{{.a = 1}, .b = 2}; // or brace the base subobject
auto b3 = B{A{1}, .b = 2}; // or initialize the base by type
// B{.b = 2, .a = 1}; // still ill-formed: out of order
The out-of-order rule from C++20 stays: designators must appear in declaration order, base members first. Ambiguous names across multiple bases are an error you have to disambiguate.
A map lookup that does not insert
operator[] on a map inserts when the key is missing, which is a footgun on a const map (it does not compile) and a silent bug when you only meant to read. find() works but hands you an iterator you have to compare against end(). P3091, “Better Lookups for map, unordered_map, and flat_map” (Halpern) adds a third option, get(), that returns an optional reference:
constexpr optional<mapped_type&> get(const key_type& k);
constexpr optional<const mapped_type&> get(const key_type& k) const;
double largest = -infinity;
for (int i = 1; i <= 100; ++i)
largest = std::max(largest, m.get(i).value_or(-infinity));
if (auto v = m.get(key)) // optional<T&>; no insertion, works on const
use(*v);
Note the name: it is get(), not lookup() (some early coverage, including the trip report’s prose, called it the latter). It returns std::optional<T&>, the reference-optional that the library gained alongside it.
Defaultable postfix increment and decrement
Everyone writes the same postfix operator: copy, call prefix, return the copy. P3668, “Defaulting Postfix Increment and Decrement Operations” lets you stop:
struct counter {
int n;
constexpr counter& operator++() { ++n; return *this; } // you write prefix
constexpr counter operator++(int) = default; // the rest is generated
};
The defaulted operator++(int) synthesizes exactly the canonical body – copy *this, call the prefix operator++, return the copy. It works with explicit object parameters and as a hidden friend too. A small thing, but it deletes a line of pure boilerplate that everyone has gotten subtly wrong at least once.
constexpr pointer tagging (with a reflection cameo)
Stashing a few bits in a pointer’s spare low bits (alignment bits) is an old trick. LLVM’s PointerIntPair is the canonical implementation. P3125, “constexpr pointer tagging” (Dusikova) standardizes it as std::pointer_tag_pair, usable at compile time:
using node_ptr = std::pointer_tag_pair<const void*, bool, 1>;
static_assert(sizeof(node_ptr) == sizeof(void*)); // the bit is free
node_ptr p{some_pointer, true};
if (p.tag())
return *static_cast<const T*>(p.pointer());
auto [ptr, tag] = p; // structured bindings work too
The detail worth noting on this blog: the default bit count is computed with pointer_bits_available(^^Ptr). That ^^Ptr is the reflection operator. A low-level bit-twiddling utility now reaches for C++26 reflection to ask a type how many spare bits its alignment guarantees. The reflection we have been writing about all month is already showing up as plumbing in unrelated library proposals.
Mandatory intptr_t
The smallest of the bunch: P3248, “Require [u]intptr_t” (Brito Gadeschi) makes intptr_t and uintptr_t (and their limit macros) mandatory instead of optional. They were optional because not every architecture could promise an integer that round-trips a pointer; in practice every target you care about has them, and proposals like the pointer-tagging one above had to defensively degrade without them. Now they can just assume them.
The pattern
None of these is a headline. Together they are the committee doing maintenance: closing the designated-initializer gap C++20 left open, giving map the lookup Python has had forever, deleting a class of hand-written operator boilerplate, standardizing a bit-packing trick everyone reimplements, and removing an optional-feature caveat. This is what a mature language looks like between its big releases – and two of these five already touch reflection or contracts, the features C++26 just shipped.
Sources: Herb Sutter, Brno trip report (June 13, 2026), which lists these as adopted for C++29. Papers (latest published revisions): P2287, Designated-initializers for Base Classes. P3091, Better Lookups for map, unordered_map, and flat_map. P3668, Defaulting Postfix Increment and Decrement Operations. P3125, constexpr pointer tagging. P3248, Require [u]intptr_t. Syntax shown is from these published revisions; the trip report cites the post-Brno revisions, which were not yet in the mailing at the time of writing.