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: место для каждого аргумента
#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
Форматирование времени (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 в рантайме.Если компилятор ещё не поддерживает 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.Практические советы
Что дальше изучать
Освоив std::format в C++20, вы заметите, как ускоряется разработка: меньше шума в коде и меньше ошибок типов. Если хотите системно подтянуть C++ (включая современные фичи стандарта и практику), рекомендую пройти качественный курс: Прокачать C++ с нуля до уровня Гуру на практическом курсе.
Итоги
std::format в C++20 — удобный, безопасный и мощный инструмент для форматирования строк. Он заменяет связку iostream+манипуляторы и устаревший printf, даёт понятный синтаксис, расширяется под ваши типы и открывает путь к ещё более лаконичному std::print в C++23. Применяйте его в логировании, отчётах, диагностике и пользовательских сообщениях — код станет чище и надёжнее уже сегодня.