std::print and std::println

std::print writes formatted text to a stream and std::println does the same with a newline. The format string is the one from lesson 063, so the specifiers carry over; what changes is that there is no std::cout in the call and no manipulator left on a stream.

The default target is stdout. Passing std::cerr or an output file as the first argument sends the text there, which makes an error message explicit in the source instead of a choice of stream variable. The functions are C++23 and the pages of this course use them where a line of output is all that is needed.

std::print writes through the C stdio layer, so output that mixes it with std::cout can interleave. The course keeps to one style per program, and each page says which one it used.

The two functions are the shortest way to write a line of output, and the pages of part 6 use them for exactly that.

println

std::println("Hello, {}!", "world") formats the arguments and writes the line with a newline. std::print is the same without the newline, and both take any number of arguments the format string names.

The program prints three lines with different types and ends with a print without a newline followed by an empty println, which is how a program ends a partial line.

The first argument is checked at compile time when it is a literal, exactly as in std::format. A mismatch is a build error rather than a run-time surprise.

#include <print> int main() { std::println("Hello, {}!", "world"); std::println("{} + {} = {}", 2, 3, 5); std::print("no newline at the end"); std::println(); }

g++ -std=c++23 -Wall -Wextra first.cpp -o first
./first

Hello, world!
2 + 3 = 5
no newline at the end

Format specs in print

The specifiers are the ones from std::format: alignment, fill, width and precision. The program aligns two words in six columns, prints a rounded fraction and formats an int in four bases.

The output shows the same bracket trick as the format lesson, and the four representations of 42: padded decimal, binary, and hexadecimal.

Because println does not use a stream object, there is no std::setw state to reset and no std::hex left switched on for the next line. Each call is independent.

#include <print> int main() { std::println("[{:>6}] [{:<6}] [{:.3f}]", "right", "left", 2.0 / 3.0); std::println("{:04d} {:b} {:x}", 42, 42, 42); }

g++ -std=c++23 -Wall -Wextra spec.cpp -o spec
./spec

[ right] [left ] [0.667]
0042 101010 2a

Writing to stderr

The first argument may be a stream: std::println(std::cerr, ...) writes to the standard error stream, and a std::ofstream writes to a file. The output stream is part of the call, not a global setting.

The program writes one line to stdout and one to stderr. The commands redirect them to two files and print each file, so the transcript shows which line went where.

Redirecting the streams is how a shell separates normal output from diagnostics, and it is why error messages belong on stderr: a pipeline that parses stdout must not receive them.

#include <iostream> #include <print> int main() { std::println("reading input.txt"); std::println(std::cerr, "error: input.txt is missing"); }

g++ -std=c++23 -Wall -Wextra stderr.cpp -o stderr_demo
./stderr_demo >out.txt 2>err.txt
cat out.txt
cat err.txt

reading input.txt
error: input.txt is missing

Containers in a line

The formatter of the standard library knows the standard containers and pairs since C++23. A vector prints as a bracketed list and a pair prints as its two members in parentheses, without a loop.

The program prints a vector of ints and a pair, and the output is the same shape a Python or Rust programmer would expect. Nested containers print nested brackets.

For a type of your own there is no default formatter: the last section shows the error. The way out is a formatter specialisation, which is beyond this lesson, or a line built with std::format from the members.

#include <print> #include <utility> #include <vector> int main() { std::vector<int> values{1, 2, 3}; std::vector<std::pair<int, int>> points{{1, 2}, {3, 4}}; std::println("{}", values); std::println("{}", points); std::println("{}", std::pair{5, 6}); }

g++ -std=c++23 -Wall -Wextra containers.cpp -o containers
./containers

[1, 2, 3]
[(1, 2), (3, 4)]
(5, 6)

A missing argument

The number of placeholders must match the number of arguments. The program has two placeholders and one argument, and the compiler reports that the format string is not a constant expression, because the check failed.

The notes inside <print> show the format string and the argument list, and the line points at the call. The mistake is visible without running anything.

The fix is to pass the second value or to remove the second placeholder. A format string built at run time skips the compile-time check and throws std::format_error instead.

#include <print> int main() { std::println("{} {}", 1); }

g++ -std=c++23 -Wall -Wextra missing.cpp -o missing

missing.cpp: In function 'int main()':
missing.cpp:5:18: error: call to consteval function 'std::basic_format_string<char, int>("{} {}")' is not a constant expression
5 | std::println("{} {}", 1);
| ^~~~~~~
In file included from /usr/include/c++/15.2.0/print:43,
from missing.cpp:1:
missing.cpp:5:18: in 'constexpr' expansion of 'std::basic_format_string<char, int>("{} {}")'
/usr/include/c++/15.2.0/format:4810:19: in 'constexpr' expansion of '__scanner.std::__format::_Checking_scanner<char, int>::std::__format::_Scanner<char>.std::__format::_Scanner<char>::_M_scan()'
/usr/include/c++/15.2.0/format:4504:30: in 'constexpr' expansion of '((std::__format::_Scanner<char>*)this)->std::__format::_Scanner<char>::_M_on_replacement_field()'
/usr/include/c++/15.2.0/format:4537:28: in 'constexpr' expansion of '((std::__format::_Scanner<char>*)this)->std::__format::_Scanner<char>::_M_pc.std::__format::_Scanner<char>::_Parse_context::std::basic_format_parse_context<char>.std::basic_format_parse_context<char>::next_arg_id()'
/usr/include/c++/15.2.0/format:313:56: error: call to non-'constexpr' function 'void std::__format::__invalid_arg_id_in_format_string()'
313 | __format::__invalid_arg_id_in_format_string();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
/usr/include/c++/15.2.0/format:259:3: note: 'void std::__format::__invalid_arg_id_in_format_string()' declared here
259 | __invalid_arg_id_in_format_string()
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Exercises

  1. Print a table of three rows with std::println and alignment specifiers.
  2. Write a program that prints a warning to stderr and a result to stdout, then separate the two streams in the shell.
  3. Print a std::vector and a std::pair with one println each.

Questions

A new set of questions in a new order every time. Choose one answer for each, then check.

  1. What is the difference between std::print and std::println?

    Answer

    println adds a newline

    The two share the format syntax, and println appends the line break.

  2. Where does std::print write by default?

    Answer

    stdout

    The default target is the standard output stream.

  3. How do you print to stderr?

    Answer

    std::print(std::cerr, ...)

    The first argument can be a stream, and the format follows.

  4. How does std::print format a vector?

    Answer

    It does not; the range must be formatted or joined

    The library has no formatter for a general vector, so the lesson builds the text.

  5. What does this program print? #include <print> int main() { std::println("Hello, world!"); std::println("{} + {} = {}", 2, 3, 2 + 3); std::print("no newline at the end"); }

    Answer

    Hello, world! / 2 + 3 = 5 / no newline at the end

    The first two calls add a newline, and the third leaves the line open.

  6. What does this program print? #include <print> int main() { std::println("[{:>6}] [{:<6}] [{:.3}]", "right", "left", 2.0 / 3.0); std::println("{:04} {:b} {:x}", 42, 42, 42); }

    Answer

    [ right] [left ] [0.667] / 0042 101010 2a

    The alignment and precision fill the first line, and the width, binary and hex specifiers the second.

  7. What does this program write to each file? #include <iostream> #include <print> int main() { std::println("reading input.txt"); std::println(std::cerr, "error: input.txt is missing"); }

    Answer

    reading input.txt to stdout, error line to stderr

    The first call uses stdout and the second passes cerr explicitly.

  8. What does this program print? #include <print> #include <string> #include <vector> int main() { std::vector<int> values{1, 2, 3}; std::print("["); for (std::size_t i = 0; i < values.size(); ++i) std::print("{}{}", i ? ", " : "", values[i]); std::println("]"); std::println("{} {}", std::string{"text"}, std::vector<int>{4, 5}.size()); }

    Answer

    [1, 2, 3] / text 2

    The loop writes the elements with separators, and the last line prints a string and a size.

  9. What does the compiler report for this call? #include <print> int main() { std::println("{} {}", 1); }

    Answer

    the format string is not a constant expression for one argument

    The format string is checked at compile time against the argument list, and one placeholder is left without an argument.

  10. What does std::print do to the stream's format state?

    Answer

    Nothing, it does not use the stream's manipulators

    The format string carries the layout, so no manipulator is stored in the stream.

The code on this page was built with g++ 15.2.0 and run on Alpine Linux 3.24.3 (x86_64) in a Docker container. The programs whose section shows a compiler error were compiled until that error and were not run.

Article author: Arthur Isaev