библиотека — вывод отформатированных строкlibrary — formatted output

std::print / std::println

std::format напрямую в stdout (или в произвольный поток) за один вызов — std::println("{} is {} years old", name, age) вместо std::cout << std::format(...) << "\n".std::format straight to stdout (or an arbitrary stream) in one call — std::println("{} is {} years old", name, age) instead of std::cout << std::format(...) << "\n".

часто используетсяcommonly used частый вопрос: чем print отличается от println и от cout << formatcommon question: how print differs from println and from cout << format

Что этоWhat it is

std::print(fmt, args...) (заголовок <print>) форматирует строку по тем же правилам, что std::format из C++20 (см. cpp20/format.html — это буквально та же грамматика плейсхолдеров {}, те же спецификаторы вроде {:.2f} или {:>10}, тот же механизм std::formatter<T> для пользовательских типов), но вместо возврата std::string пишет результат напрямую в stdout. std::println(fmt, args...) — то же самое плюс автоматический перевод строки в конце, аналогично тому, как puts в C добавляет '\n', а fputs — нет.std::print(fmt, args...) (from <print>) formats a string using the exact same rules as C++20's std::format (see cpp20/format.html — it's literally the same {} placeholder grammar, the same specifiers like {:.2f} or {:>10}, the same std::formatter<T> mechanism for user-defined types), but instead of returning a std::string it writes the result directly to stdout. std::println(fmt, args...) is the same thing plus an automatic trailing newline, the same relationship as C's puts (adds '\n') versus fputs (doesn't).

до C++23 — format + cout как обходной путьbefore C++23 — format + cout as a workaround
std::cout << std::format("{} is {} years old", name, age) << "\n";
// строится промежуточный std::string, затем копируется в поток;// an intermediate std::string is built, then copied into the stream;
// перевод строки — отдельный литерал, легко забыть или продублировать// the newline is a separate literal — easy to forget or duplicate
C++23 — println напрямуюC++23 — println directly
std::println("{} is {} years old", name, age);
// ни одной промежуточной строки — форматирование и вывод за один вызов,// no intermediate string at all — formatting and output in one call,
// перевод строки добавлен автоматически// the newline is added automatically

Зачем ввелиWhy it was introduced

C++20 дал типобезопасное форматирование, но не дал прямого способа его напечатать — разрыв закрыли в следующем же стандарте:C++20 delivered type-safe formatting but no direct way to print it — the gap was closed in the very next standard:

01

std::format ничего не печатает — вывод всегда требовал обёрткиstd::format prints nothing — output always required a wrapper

std::format возвращает std::string, поэтому вывод на экран в C++20 неизбежно означал std::cout << std::format(...) — многословно для настолько частой операции, как строка лога или отладочная печать, и требует лишней аллокации/копии строки только ради того, чтобы её тут же отдать потоку и выбросить.std::format returns a std::string, so printing to screen in C++20 inevitably meant std::cout << std::format(...) — verbose for an operation as common as a log line or a debug print, and it forces an extra string allocation/copy just to hand it to the stream and immediately discard it.

02

std::cout исторически не всегда корректно печатает Unicode в консольstd::cout hasn't always printed Unicode to the console correctly

На некоторых платформах (в первую очередь Windows) вывод многобайтных UTF-8 строк через std::cout в консоль исторически требовал ручной настройки кодовой страницы или обходных путей — std::print, где это возможно, использует платформенные API печати напрямую, что на таких реализациях решает проблему корректного вывода Unicode без дополнительных телодвижений со стороны разработчика.On some platforms (Windows above all), printing multi-byte UTF-8 strings to the console via std::cout has historically required manual code-page configuration or workarounds — where possible, std::print uses platform printing APIs directly, which on such implementations fixes correct Unicode output with no extra effort from the developer.

03

Прямой вывод в целевой поток может быть эффективнее, чем строка-посредникDirect output to the target stream can be faster than a string intermediary

Формируя и печатая результат в одном вызове, реализация вправе писать сразу в буфер потока вывода, минуя выделение отдельного std::string и последующее копирование его содержимого — для консольного вывода это может быть заметно эффективнее и корректнее по кодировке, чем не оптимизированная под конкретную платформу связка std::cout << std::format(...).By building and printing the result in a single call, the implementation is free to write straight into the output stream's buffer, skipping a separate std::string allocation and the subsequent copy of its contents — for console output this can be noticeably more efficient and encoding-correct than a std::cout << std::format(...) pairing that isn't optimized for the target platform.

Как это работает под капотомHow it works under the hood

Базовая перегрузка пишет в stdout; есть перегрузка с явным первым параметром std::FILE* для вывода в произвольный C-поток, и перегрузка с std::ostream& для вывода в iostream-поток (например, std::cerr или файловый std::ofstream). Строка формата, как и у std::format, должна быть compile-time константой для базовых перегрузок — компилятор проверяет число и типы аргументов относительно плейсхолдеров ещё до генерации кода.The base overload writes to stdout; there's an overload with an explicit first std::FILE* parameter for output to an arbitrary C stream, and an overload with std::ostream& for output to an iostream (say, std::cerr or a file-backed std::ofstream). Like with std::format, the format string must be a compile-time constant for the base overloads — the compiler checks the placeholder count and argument types before any code is even generated.

#include <print>

std::print("{} is {} years old\n", name, age);   // stdout, перевод строки — вручную// stdout, newline written by hand
std::println("{} is {} years old", name, age);      // stdout, перевод строки — автоматически// stdout, newline added automatically

std::print(stderr, "warning: {}\n", msg);            // явный FILE* — вывод в stderr// explicit FILE* — output to stderr

std::ofstream log_file("app.log");
std::println(log_file, "[{}] {}", level, message);    // явный ostream& — вывод в файл// explicit ostream& — output to a file

Если строка формата не известна на этапе компиляции (получена из конфигурации, из аргумента командной строки, собрана в рантайме), базовая перегрузка std::print в такой вызов не скомпилируется — так же, как и std::format. Для этого случая есть std::vprint_unicode (и std::vprint_nonunicode), принимающие уже упакованные аргументы через std::make_format_args — прямая параллель с std::vformat из C++20 (см. gotcha про vformat в cpp20/format.html).If the format string isn't known at compile time (it comes from configuration, from a command-line argument, or is assembled at runtime), the base std::print overload simply won't compile for that call — just like std::format. For that case there's std::vprint_unicode (and std::vprint_nonunicode), which take pre-packed arguments via std::make_format_args — a direct parallel to C++20's std::vformat (see the vformat gotcha in cpp20/format.html).

Примеры из реального кодаReal-world examples

типичные сценарии использованияtypical usage scenarios
01

Строка лога без промежуточного std::stringA log line with no intermediate std::string

Замена типичного std::cout << std::format(...) в горячем пути логирования — то же форматирование, но без лишней аллокации строки, которая тут же выбрасывается.Replacing the typical std::cout << std::format(...) on a logging hot path — the same formatting, but without an extra string allocation that gets thrown away immediately.

int order_id = 4821;
std::string_view status = "filled";

std::println("order {} status={}", order_id, status);
// "order 4821 status=filled" — напечатано в stdout, перевод строки автоматический// "order 4821 status=filled" — printed to stdout, newline automatic
02

Вывод предупреждений в stderr через явный FILE*Printing warnings to stderr via an explicit FILE*

Диагностика утилиты командной строки должна идти в stderr, а не в stdout, чтобы не смешиваться с полезным выводом программы при перенаправлении в файл или пайп.A command-line tool's diagnostics must go to stderr, not stdout, so they don't mix with the program's useful output when it's redirected to a file or piped.

void warn(std::string_view msg) {
    std::println(stderr, "warning: {}", msg);
}

warn("config value 'timeout' missing, using default");
03

Табличный вывод в консоль с выравниваниемAligned tabular output to the console

Та же задача, что в примере со std::format для табличного вывода (см. cpp20/format.html) — здесь без промежуточной строки и без явного std::cout, спецификаторы выравнивания и ширины те же самые.The same table-printing task as in the std::format example (see cpp20/format.html) — here with no intermediate string and no explicit std::cout, using the exact same alignment and width specifiers.

struct Row { std::string_view name; int qty; };
std::vector<Row> rows = {{"apples", 12}, {"bananas", 3}};

for (const auto& r : rows) {
    std::println("{:<10}{:>5}", r.name, r.qty);
}
// "apples         12"// "apples         12"
// "bananas         3"// "bananas         3"
04

Запись отформатированных строк в лог-файл через ostreamWriting formatted lines to a log file via an ostream

Тот же std::print/std::println, но с явно указанным потоком назначения — удобно, когда логика логирования уже принимает std::ostream& как параметр и должна работать одинаково для файла, std::cerr или строкового потока в тестах.The same std::print/std::println, but with an explicitly named destination stream — handy when logging logic already accepts a std::ostream& parameter and must behave identically for a file, std::cerr, or a string stream in tests.

void log_line(std::ostream& out, std::string_view level, std::string_view msg) {
    std::println(out, "[{}] {}", level, msg);
}

std::ofstream file("app.log", std::ios::app);
log_line(file, "INFO", "startup complete");

ГраблиGotchas

Смешивание std::print и std::cout без синхронизации даёт неожиданный порядок выводаMixing std::print and std::cout without synchronization gives an unexpected output order

std::print/std::println по умолчанию пишут в stdout, но путь и буферизация этой записи не обязаны совпадать с тем, как буферизует свой вывод std::cout — если в одной программе вперемешку вызывать оба API без явной синхронизации потоков (std::ios::sync_with_stdio), строки могут напечататься не в том порядке, в каком были вызваны. Правило простое: в рамках одной программы (или хотя бы одного участка кода) выбрать один способ вывода и не смешивать print/println с cout << на один и тот же поток.std::print/std::println write to stdout by default, but the path and buffering of that write aren't guaranteed to match how std::cout buffers its own output — mixing both APIs interleaved in one program without explicit stream synchronization (std::ios::sync_with_stdio) can print lines out of the order they were called in. The simple rule: pick one output method within a program (or at least one section of code) and don't mix print/println with cout << on the same stream.

Строка формата должна быть compile-time константой, как и у std::formatThe format string must be a compile-time constant, just like with std::format

Ровно то же ограничение, что у std::format (см. cpp20/format.html): базовая перегрузка ожидает строку формата как значение, конструируемое из constexpr-литерала, а не как рантайм-std::string. Динамическая строка формата (полученная из конфигурации, введённая пользователем) требует std::vprint_unicode/std::vprint_nonunicode с явно упакованными аргументами через std::make_format_args — другая сигнатура, тот же обходной путь, что vformat для format.Exactly the same restriction as with std::format (see cpp20/format.html): the base overload expects the format string as a value constructible from a constexpr literal, not as a runtime std::string. A dynamic format string (from configuration, from user input) requires std::vprint_unicode/std::vprint_nonunicode with explicitly packed arguments via std::make_format_args — a different signature, the same kind of workaround as vformat is for format.

print не заменяет format — иногда всё ещё нужна именно строкаprint doesn't replace format — sometimes you still need the actual string

Если отформатированный результат нужен не для немедленного вывода, а для дальнейшей обработки (сохранить в переменную, передать в исключение, отправить по сети), std::format остаётся правильным инструментом — std::print/std::println закрывают только случай "отформатировать и сразу напечатать", не заменяя format как таковой.If the formatted result isn't needed for immediate output but for further processing (stored in a variable, passed into an exception, sent over the network), std::format remains the right tool — std::print/std::println only cover the "format and print right away" case, they don't replace format as such.