C++26 Preview
C++26 is the next version of the C++ standard, and it is the largest update since C++11. It adds reflection (code that can inspect other code while compiling), contracts (checked preconditions and postconditions), a standard framework for asynchronous work called std::execution, and several changes that make existing code safer simply by recompiling it.
This lesson explains where C++26 stands today, what its headline features look like, and which compilers already let you experiment. Treat it as a preview: the details may still change before the standard is officially published, and the code here needs very new compilers.
Where C++26 stands (October 2026)
- The ISO C++ committee (WG21) finished its technical work on the C++26 draft at its meeting in London (Croydon), UK, in March 2026.
- The draft then went to an international approval ballot (the DIS ballot) among national standards bodies. That ballot closed on 23 October 2026 and was reported as not approved. Committee members link most of the "no" votes to disagreement about contracts.
- WG21 meets next in Búzios, Brazil, from 16 to 21 November 2026, where it is expected to decide how to proceed.
So C++26 is not yet an official ISO standard. C++23 (ISO/IEC 14882:2024) remains the current published standard, which is why this course teaches C++23. The features below are in the draft as finalised by WG21; contracts in particular could still change.
Static reflection
Reflection lets a program ask questions about its own types while compiling: "what is this type called?", "what members does this struct have?", "what values does this enum contain?". The new operator ^^ turns a name into a reflection value of type std::meta::info, and functions in the std::meta namespace (header <meta>) query it.
#include <meta>
#include <print>
#include <string_view>
enum class Coach { sleeper, ac3, ac2, ac1 };
struct Ticket {
int number;
Coach coach;
};
int main() {
constexpr std::string_view type_name = std::meta::identifier_of(^^Ticket);
constexpr auto coach_count = std::meta::enumerators_of(^^Coach).size();
std::println("{} has a coach type with {} values", type_name, coach_count);
}Ticket has a coach type with 4 values
Everything is computed by the compiler, so it costs nothing at run time. Reflection also includes splicers, written [: r :], which turn a reflection back into code, and C++26 expansion statements (template for) loop over lists of reflections at compile time. Together they will let libraries generate code that is written by hand today: converting an enum to text (as you did in Enums & enum class), printing every member of a struct, or saving objects to JSON without repeating each member name.
GCC 16, released on 30 April 2026, implements reflection; enable it with -std=c++26 -freflection.
Contracts: pre, post and contract_assert
A contract states what must be true when a function is called (a precondition), what it guarantees when it returns (a postcondition), and what must hold at a point inside it (an assertion). Until now, C++ programmers wrote these as comments or with the assert macro. C++26 makes them part of the function declaration:
double withdraw(double balance, double amount)
pre(amount > 0)
pre(amount <= balance)
post(result: result >= 0)
{
contract_assert(balance >= 0);
return balance - amount;
}pre and post appear between the function's signature and its body; post(result: ...) names the return value so the condition can check it. contract_assert replaces the assert macro inside function bodies. Because the conditions are part of the declaration, callers, tools and compilers can see them. How a contract is checked is chosen when you build the program: the draft defines evaluation semantics called ignore, observe, enforce and quick-enforce. With enforce, a violated contract calls a violation handler and then stops the program instead of continuing with bad data. GCC 16 implements contracts as well.
std::execution: senders and receivers
C++11 gave you threads and std::async, but no standard way to describe chains of asynchronous work or to say where work should run. std::execution provides that framework. Work is described by senders, combined with algorithms such as just, then and when_all, and started on schedulers such as a thread pool.
#include <execution>
#include <print>
#include <utility>
namespace ex = std::execution;
int main() {
auto work = ex::just(21)
| ex::then([](int x) { return x * 2; });
auto [answer] = std::this_thread::sync_wait(std::move(work)).value();
std::println("Answer: {}", answer);
}Answer: 42
just(21) creates a sender that produces 21, then attaches a step, and sync_wait starts the work and waits for its result. Nothing runs until the work is started, much like a lazy view. Standard library support is still incomplete in 2026; the open-source reference implementation, NVIDIA's stdexec library, lets you try the same model today.
Safer by default
Two C++26 changes improve safety without changing your code:
- Erroneous behaviour for uninitialised variables. In C++23, reading a local variable that was never initialised is undefined behaviour. In C++26 it becomes erroneous behaviour: the variable holds some value, the program is wrong, but the compiler may no longer assume it "cannot happen" and optimise in surprising ways. Tools are encouraged to diagnose it. The attribute
[[indeterminate]]opts out where performance requires it. - Library hardening. A hardened standard library implementation checks many preconditions, for example that an index passed to
std::vector::operator[],std::spanorstd::stringis in range, and stops the program instead of reading random memory. Standard libraries already offer similar modes today, such as GCC's-D_GLIBCXX_ASSERTIONS.
Smaller C++26 features
C++26 also contains many smaller conveniences. A few that you are likely to meet:
void close_account(int id) = delete("use archive_account() instead"); // reason in the error
auto [_, marks] = find_student("Isha"); // _ ignores a value you do not need
std::inplace_vector<int, 8> recent_scores; // vector with fixed capacity, no heap memory| Feature | What it does |
|---|---|
= delete("reason") | A deleted function can explain why in the compiler error |
_ placeholder | Names a variable you do not intend to use, without warnings |
Pack indexing Ts...[0] | Picks one element from a template parameter pack |
#embed | Includes the bytes of a file, such as an image, directly in the program |
std::inplace_vector | A vector with a fixed maximum size and no heap allocation |
Saturation arithmetic (std::add_sat) | Integer maths that stops at the limit instead of overflowing |
std::hive, std::simd, std::linalg | A container with stable positions, data-parallel types, linear algebra |
Key points
- C++26 is the next C++ standard; WG21 finished the draft in March 2026, but the October 2026 ISO ballot did not approve it, so it is not yet official.
- Reflection uses
^^andstd::metafunctions to inspect types at compile time; GCC 16 supports it with-freflection. - Contracts add
pre,postandcontract_assertto express and check function requirements. std::executiondescribes asynchronous work as composable senders run on schedulers.- Uninitialised reads become erroneous behaviour and hardened libraries add bounds checks, improving safety without code changes.
Exercise
Write a function int seats_left(int capacity, int booked) for a train coach. First write it in C++26 style with preconditions (capacity is positive, booked is between 0 and capacity) and a postcondition (the result is not negative). Then write a C++23 version that you can compile today, checking the same conditions with assert from <cassert>, and print the seats left for a capacity of 72 with 50 booked.
Show solution
// C++26 draft version
int seats_left(int capacity, int booked)
pre(capacity > 0)
pre(booked >= 0 && booked <= capacity)
post(result: result >= 0)
{
return capacity - booked;
}// C++23 version that compiles today
#include <cassert>
#include <print>
int seats_left(int capacity, int booked) {
assert(capacity > 0);
assert(booked >= 0 && booked <= capacity);
int result = capacity - booked;
assert(result >= 0);
return result;
}
int main() {
std::println("Seats left: {}", seats_left(72, 50));
}Seats left: 22
The C++26 version puts the requirements in the declaration, where callers and tools can see them. The assert version checks the same things but only inside the body, and assert checks disappear completely when NDEBUG is defined, which is common in release builds.
Quick Quiz
1. What is the status of C++26 at the end of October 2026?
The draft was completed in March 2026; the DIS ballot that closed on 23 October 2026 was reported as not approved.
2. Which operator creates a reflection of a type in C++26?
^^Ticket produces a std::meta::info value describing the type Ticket.
3. Which keywords express contracts in C++26?
Preconditions use pre, postconditions post, and assertions inside functions contract_assert.
4. What changes for reading an uninitialised local variable in C++26?
Erroneous behaviour means the program is wrong but no longer has undefined behaviour from this read.
Learn C++ with a mentor
Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
