Everyone is familiar with std::cout. However, C++ provides 2 other output streams that can be used for a different purpose.
Three things to take away:
std::coutcarries user-facing outputstd::cerrcarries diagnostics and errorsstd::clogisstd::cerr‘s buffered cousin
What’s different between the three streams
All three streams write text to the program’s output, but they differ in two important ways:
- The destination the OS routes them to
- How aggressively they flush their buffers
All three streams write text to the program’s output, but they differ in two important ways: the destination the OS routes them to, and how aggressively they flush their buffers.
| Stream | Destination | Buffering | Typical use |
|---|---|---|---|
std::cout | stdout (fd 1) | Buffered | Normal program output |
std::cerr | stderr (fd 2) | Unbuffered | Errors, urgent diagnostics |
std::clog | stderr (fd 2) | Buffered | Logs, high-volume tracing |
std::cout is the workhorse. It buffers its output for performance — characters accumulate in memory and are written to the terminal in chunks. The flush usually happens automatically (when the buffer fills, when the program exits, when std::endl is used)
std::cerr is unbuffered, which means every write is sent to the OS immediately. This ensures the error messages are displayed immediately, even if the program crashes. With std::cerr, the message is pushed to the console the millisecond the code executes. This is extremely useful in capturing all debug output (at the cost of some performance). Use std::cerr for diagnostic information, warnings and errors – information that isn’t useful to the user and that should be abstracted.
// Validate input
if (monthNumber < 1 || monthNumber > 12) {
std::cerr << "Please enter a valid month!\\n";
return;
}
std::clog writes to the same destination as std::cerr (the standard error stream), but is buffered like std::cout. Use it for high-volume diagnostics.
When to use std::clog
std::clog is the least-used of the three but earns its place in programs that emit a lot of diagnostic output. Imagine a long- running data processor that logs each batch it completes:
for (const auto& batch : batches) {
std::clog << "Processing batch " << batch.id << '\\n';
process(batch);
}
Using std::cerr here would flush after every line — many thousands of system calls over the course of the run. Using std::cout pollute the program output, breaking redirection.
std::clog is the right answer: buffered for performance, but routed to stderr so the user can filter or capture it separately from real results.
The trade-off is that buffered output may not appear until the buffer fills or the program exits cleanly
In Conclusion
C++ provides three output streams because programs produce three kinds of output:
- Results
- Errors
- Logs
Use std::cout for the program’s real output, std::cerr for errors that must reach the user immediately, and std::clog for high-volume diagnostics where flushing every line would hurt