1. Problem It Solves
C++23 extends formatting beyond scalar values. Standard range, tuple-like, and thread-id formatters let diagnostics describe structured data without manual loops or string assembly. The goal is to make the relevant rule visible in code instead of relying on an assumption about what “C++23 support” means.
2. Prerequisites
Day 5: ranges and iteration.
Day 6: threads.
Day 35: modern formatted output.
3. Core Idea
A formatter is a display lens. Range and tuple formatters recursively apply lenses to their elements, while the thread-id formatter gives an implementation-defined but printable identity. Read the syntax from left to right, identify the value, object, or range being transformed, and then check its resulting type, lifetime, or ownership. Standardization and implementation are separate: a C++23 mode may still lack one library component, so a feature-test macro is part of responsible portable use.
4. Minimal Syntax
auto text = std::format("values={} pair={}", values, pair);5. How It Works
The sample builds the smallest expression or object needed for Formatting Ranges, Tuples, and Thread IDs.
It formats fixed structured values when all C++23 formatters are present. The compiler and library apply the relevant rule before the program observes the result.
The program prints or checks a readable structure or an exact unsupported-library message, making the important behavior easy to verify.
6. Common Mistakes
Assuming the textual thread ID is stable across runs is wrong; deeply nested range formatting can also create large logs and nontrivial formatting cost.
Enabling C++23 mode without checking the relevant feature macro may select code that the installed compiler or standard library does not implement yet.
7. When to Use It
Use it when the task involves diagnostics and compact display of small structured standard-library values.
Avoid it when the task involves machine-readable serialization, where a specified format and escaping rules are required.
8. Simple Example
A debug line shows a worker ID, its (task, priority) tuple, and a short range of measurements. The downloadable program keeps the data fixed so the output can be compared without entering input.
Complete sample code
Source file
cpp23/36_format_ranges_tuples_thread_ids/main.cpp
#include <iostream>
#include <thread>
#include <tuple>
#include <vector>
#include <version>
#if __has_include(<format>)
#include <format>
#endif
int main() {
std::vector values{1, 2, 3};
std::tuple pair{7, 8};
#if defined(__cpp_lib_format_ranges) && defined(__cpp_lib_formatters)
std::cout << std::format("values={} pair={} thread={}",
values, pair, std::this_thread::get_id())
<< '\n';
#else
(void)values;
(void)pair;
std::cout << "C++23 structured formatters unavailable\n";
#endif
}
9. Key Takeaways
Separate the language rule from compiler and library availability.
Keep lifetime, ownership, and deduced types visible when they affect correctness.
Prefer the smallest syntax that communicates the intent.
Guard facilities that are not yet uniformly implemented.
10. Self-Check Questions
Easy — What is the smallest syntax in Section 4, and what main job does it perform?
Medium — Read the sample program: which value, type, or branch is observed, and why?
Hard — If a range's element type has no formatter, why does formatting the range fail even though the outer range type itself is recognized?