std::print does not tear the way std::cout does
The fixes so far took work. std::osyncstream needs every writer to cooperate; the static std::mutex needs you to hold a lock around each write. C++23’s std::print gives you the common case for free.
A single std::print or std::println call does not interleave with other calls, the way printf never did and std::cout << always could. No syncstream, no mutex, nothing to remember: one call, one whole line.
The top half is three threads calling std::println once per line, and every line is intact. That is the guarantee. The bottom half shows its edge: when one logical line is built from two calls, std::print for the prefix and std::println for the rest, each call is atomic but nothing binds them together, so another thread’s line lands in the gap. The prefixes and suffixes splice exactly like the raw std::cout version from the first post.
How C++23 pulled it off
std::print formats its arguments into a temporary string and writes that whole string to the stream in a single operation, while the underlying C stream locks itself for the length of that write. One call therefore reaches the terminal as one uninterrupted sequence. It is the same mechanism printf has always had, brought to type-safe formatting.
The lesson for a logger is simple: build each log line in a single std::print call. Pass the whole line as one format string with all its arguments and you get non-interleaved output with nothing else to set up. Split a line across calls and you are back to needing a syncstream or a lock to group them.
That per-call guarantee has a deeper story underneath, and C++26 is still refining it.
Sources: P2093 (formatted output) · Victor Zverovich: std::print in C++23.