библиотека — низкоуровневая конвертацияlibrary — low-level conversion
Конвертация число↔строка без локали, без исключений, без аллокаций — пишет прямо в переданный буфер, на порядок быстрее printf/sstream и быстрее даже to_string/stoi.Number↔string conversion with no locale, no exceptions, no allocations — writes directly into a caller-supplied buffer, an order of magnitude faster than printf/sstream and faster even than to_string/stoi.
Заголовок <charconv> даёт две низкоуровневые функции. std::to_chars(first, last, value) пишет текстовое представление value в диапазон [first, last) и возвращает структуру to_chars_result с полями ptr (указатель сразу после последнего записанного символа) и ec (код ошибки, например если буфер оказался мал). std::from_chars(first, last, value) делает обратное — парсит число из [first, last) в переменную value и возвращает from_chars_result с теми же по смыслу полями ptr/ec. Обе функции не аллоцируют память, не бросают исключений и не учитывают локаль — результат всегда в "C"-формате независимо от текущей локали программы.The <charconv> header provides two low-level functions. std::to_chars(first, last, value) writes a textual representation of value into the range [first, last) and returns a to_chars_result struct with fields ptr (a pointer just past the last character written) and ec (an error code, e.g. if the buffer was too small). std::from_chars(first, last, value) does the reverse — parses a number from [first, last) into the value variable and returns a from_chars_result with analogous ptr/ec fields. Neither function allocates memory, throws exceptions, or honors the locale — the result is always in "C" format regardless of the program's current locale.
char buf[16];
std::sprintf(buf, "%d", 42);
// учитывает локаль, парсит форматную строку — накладные расходы даже для "C"-локали// honors locale, parses a format string — overhead even for the "C" locale
std::string s = std::to_string(42);
// быстрее printf, но аллоцирует std::string — нет контроля над буфером// faster than printf, but allocates a std::string — no control over the buffer
#include <charconv>
char buf[16];
auto [ptr, ec] = std::to_chars(buf, buf + 16, 42);
if (ec == std::errc{}) {
// buf содержит "42" от начала до ptr, без аллокаций и локали// buf holds "42" from the start up to ptr, no allocations, no locale
}
std::sprintf("%d", n) и std::stringstream всегда проходят через механизм локализации (даже если фактически используется локаль "C") плюс парсинг форматной строки или работу потокового буфера — накладные расходы, которые не устраняются одним лишь выбором "простой" локали, они встроены в саму архитектуру этих API.std::sprintf("%d", n) and std::stringstream always go through the localization machinery (even when the "C" locale is effectively in use) plus format-string parsing or stream-buffer overhead — costs that aren't eliminated just by picking a "simple" locale; they're baked into these APIs' architecture.
std::to_string возвращает новый std::string при каждом вызове — аллокация на куче (за вычетом small-string-optimization для коротких чисел) в коде, который может вызываться миллионы раз в секунду, ощутимо дороже, чем запись в уже существующий буфер. std::stoi дополнительно бросает исключение при ошибке разбора, что тоже стоит дороже, чем проверка кода ошибки.std::to_string returns a brand-new std::string on every call — a heap allocation (short-string optimization for small numbers aside) in code that might be called millions of times a second is noticeably more expensive than writing into an already-existing buffer. std::stoi additionally throws an exception on a parse failure, which is also costlier than checking an error code.
Для действительно горячего кода — например, сериализации большого потока чисел (цены и объёмы в market data, компактные бинарные/текстовые протоколы, логирование под нагрузкой) — ни один из предыдущих вариантов не подходил полностью: printf/sstream медленные из-за локали и форматирования, to_string/stoi всё ещё аллоцируют. from_chars/to_chars закрывают именно эту нишу: минимальный набор гарантий (locale-independent, no throw, no allocation), максимальная скорость.For genuinely hot code — say, serializing a high-volume stream of numbers (prices and volumes in market data, compact binary/text protocols, logging under load) — none of the earlier options fit fully: printf/sstream are slow due to locale and formatting, to_string/stoi still allocate. from_chars/to_chars fill exactly this niche: a minimal set of guarantees (locale-independent, no throw, no allocation), maximum speed.
#include <charconv>
#include <string_view>
// число -> строка:// number -> string:
char buf[32];
auto [ptr, ec] = std::to_chars(buf, buf + sizeof(buf), 3.14159);
if (ec == std::errc{}) {
std::string_view result(buf, ptr - buf); // без лишней аллокации// no extra allocation
}
// строка -> число:// string -> number:
std::string_view text = "42.5 remaining";
double value{};
auto [ptr2, ec2] = std::from_chars(text.data(), text.data() + text.size(), value);
if (ec2 == std::errc{}) {
// value == 42.5, ptr2 указывает сразу после "42.5", на пробел// value == 42.5, ptr2 points right after "42.5", at the space
} else if (ec2 == std::errc::invalid_argument) {
// строка не начинается с числа вообще// the string doesn't start with a number at all
}
Результат возвращается через структуру (to_chars_result/from_chars_result), а не через возвращаемое значение самой конвертации напрямую — удобно разбирать через structured bindings (auto [ptr, ec] = ...), но непривычно тем, кто ожидает поведения в духе int n = std::stoi(s).The result comes back via a struct (to_chars_result/from_chars_result) rather than as the converted value's return type directly — convenient to unpack via structured bindings (auto [ptr, ec] = ...), but unfamiliar to anyone expecting behavior like int n = std::stoi(s).
В отличие от sprintf (возвращает число записанных символов) и stoi (возвращает само число), to_chars/from_chars возвращают структуру, из которой нужно явно прочитать ec, чтобы понять, была ли ошибка, и ptr, чтобы понять, где закончился результат. Забыть проверить ec — не ошибка компиляции, но код молча продолжит работать с некорректным результатом при сбое конвертации.Unlike sprintf (returns the count of characters written) and stoi (returns the number itself), to_chars/from_chars return a struct from which ec must be explicitly read to know whether there was an error, and ptr to know where the result ends. Forgetting to check ec isn't a compile error, but the code will silently keep working with an invalid result if the conversion failed.
В отличие от std::stoi/strtol, которые молча пропускают ведущие пробелы перед числом, from_chars ожидает, что строка начинается сразу с числа — from_chars(" 42", ...) вернёт ec == std::errc::invalid_argument, потому что первый символ (пробел) не может быть частью числа. Код, портированный со stoi на from_chars, должен явно обрезать входные пробелы заранее, если они возможны.Unlike std::stoi/strtol, which silently skip leading whitespace before the number, from_chars expects the string to start with the number immediately — from_chars(" 42", ...) returns ec == std::errc::invalid_argument, because the first character (a space) can't be part of a number. Code ported from stoi to from_chars must explicitly trim leading whitespace beforehand if it's possible in the input.
Целочисленные перегрузки to_chars/from_chars были реализованы во всех крупных стандартных библиотеках практически сразу, но версии для float/double появлялись заметно позже — libstdc++ и libc++ добавляли их поэтапно уже после релиза C++17. Перед использованием floating-point конвертации на конкретном проекте стоит явно проверить, поддерживает ли её используемая версия компилятора/стандартной библиотеки, а не полагаться на "раз это C++17, значит работает".The integer overloads of to_chars/from_chars were implemented in essentially all major standard libraries right away, but the float/double versions showed up noticeably later — libstdc++ and libc++ added them in stages well after C++17 shipped. Before relying on floating-point conversion in a given project, it's worth explicitly checking whether the compiler/standard library version in use supports it, rather than assuming "it's C++17, so it must work."