std::format в C++20: современное форматирование строк с примерами и лучшими практиками
Если вам надоело возиться с iostream‑манипуляторами и опасными printf, самое время освоить std::format в C++20. Он даёт привычный форматный синтаксис, типобезопасность и производительность. Разберёмся на практических примерах и типичных ошибках, чтобы вы смогли сразу применять std::format в рабочих проектах.
Быстрый старт: как подключить и скомпилировать
Поддержка std::format есть в современных компиляторах (GCC 13+, Clang 16+, MSVC 19.3x+). Минимальный пример:
#include <format>
#include <iostream>
int main() {
std::string name = "Алиса";
auto s = std::format("Привет, {}!", name);
std::cout << s << '\n';
}
Компиляция (GCC/Clang):
g++ -std=c++20 main.cpp -O2 -o app
В MSVC достаточно включить стандарт C++20 в настройках проекта.
Синтаксис std::format: место для каждого аргумента
- Плейсхолдер {} — вставка значения по порядку аргументов.
- После двоеточия задаётся спецификатор формата: {<spec>}.
- Внутри можно указывать выравнивание, ширину, заполнитель, систему счисления, точность и знак.
#include <format>
#include <iostream>
int main() {
// Выравнивание и ширина
std::cout << std::format("[{:<8}] [:{:^8}] [:{:>8}]\n", 42, 42, 42);
// Заполнитель (fill)
std::cout << std::format("{:*^10}\n", "C++"); // ***C++***
// Числа: ведущие нули, знак, системы счисления
std::cout << std::format("{:08d}\n", 123); // 00000123
std::cout << std::format("{:+d}\n", 5); // +5
std::cout << std::format("{:#x}\n", 255); // 0xff
std::cout << std::format("{:#b}\n", 10); // 0b1010
// Вещественные числа и точность
std::cout << std::format("{:.2f}\n", 3.14159); // 3.14
std::cout << std::format("{:.3e}\n", 12345.0); // 1.235e+04
}
std::format vs printf и iostream
- Типобезопасность: несовпадение типов ловится на этапе компиляции, если формат — строковый литерал (используется форматный тип format_string).
- Удобство: один стиль форматирования вместо десятков манипуляторов iostream.
- Производительность: сравнима с printf, при этом без опасных UB из‑за неверных спецификаторов.
Форматирование времени (chrono)
В C++20 можно красиво форматировать время. Чаще всего достаточно округлить до секунд и задать шаблон:
#include <format>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
auto now = system_clock::now();
auto s = std::format("{:%Y-%m-%d %H:%M:%S}", floor<seconds>(now));
std::cout << s << '\n';
}
Шаблоны формата схожи с strftime: %Y — год, %m — месяц, %d — день, %H:%M:%S — время.
Печать без cout: std::print из C++23
Если у вас C++23, можно печатать напрямую:
#include <print>
int main() {
std::print("Hello, {}!\n", "world");
}
В C++20 это ещё недоступно — пользуйтесь std::format + std::cout.
Собственный тип: пишем форматтер за 2 минуты
std::format умеет форматировать ваши структуры, если вы напишете специализацию std::formatter. Пример для точки:
#include <format>
#include <iostream>
struct Point { int x; int y; };
template<>
struct std::formatter<Point> {
// Простой форматтер: игнорируем формат-спеки
constexpr auto parse(std::format_parse_context& ctx) { return ctx.begin(); }
template<class FormatContext>
auto format(const Point& p, FormatContext& ctx) const {
return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
}
};
int main() {
Point p{3, 4};
std::cout << std::format("P = {}\n", p);
}
Так вы можете описывать читаемый вывод для доменных сущностей и логов, не засоряя код ручной сборкой строк.
Типичные ошибки и как их избежать
- Несовпадение типов и спецификаторов. Со строковым литералом компилятор укажет на ошибку; с динамической строкой будет выброшено исключение
std::format_errorв рантайме. - Забыли подключить заголовок: нужен <format> (и иногда <chrono> для времени).
- Старая версия компилятора. Обновитесь до GCC 13+/Clang 16+/актуального MSVC или временно используйте {fmt}.
Если компилятор ещё не поддерживает std::format
Можно подключить библиотеку {fmt}, на которой основан стандартный std::format. API практически совпадает, а переход позже займёт минуты:
#include <fmt/core.h>
#include <fmt/chrono.h>
int main() {
fmt::print("Привет, {}!\n", "мир");
}
Небольшая памятка по спецификаторам
- Выравнивание:
":<width"(влево),":^width"(по центру),":>width"(вправо). Заполнитель указывается перед выравниванием:":*^10". - Числа:
d(10‑я),x/X(16‑я),b(2‑я), префикс#добавляет 0x, 0b. Ведущие нули:08d. Знак:+для положительных тоже. - Вещественные:
f(фикс),e(экспонента),g(умно выбирает), точность:.N. - Строки и char форматируются без танцев с бубном: просто {} или с выравниванием.
Практические советы
- Выносите форматные строки в константы — проще переиспользовать и тестировать.
- Для логирования определяйте форматтеры доменных типов — логи станут короче и понятнее.
- Не смешивайте старые printf с std::format: держите проект в едином стиле форматирования.
- Проверяйте поддержку на CI разными компиляторами, чтобы не поймать сюрпризы.
Что дальше изучать
Освоив std::format в C++20, вы заметите, как ускоряется разработка: меньше шума в коде и меньше ошибок типов. Если хотите системно подтянуть C++ (включая современные фичи стандарта и практику), рекомендую пройти качественный курс: Прокачать C++ с нуля до уровня Гуру на практическом курсе.
Итоги
std::format в C++20 — удобный, безопасный и мощный инструмент для форматирования строк. Он заменяет связку iostream+манипуляторы и устаревший printf, даёт понятный синтаксис, расширяется под ваши типы и открывает путь к ещё более лаконичному std::print в C++23. Применяйте его в логировании, отчётах, диагностике и пользовательских сообщениях — код станет чище и надёжнее уже сегодня.
-
Создано 13.05.2026 17:04:55
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.