библиотека — типы общего назначенияlibrary — general-purpose types

std::string_view

Невладеющее, неизменяемое представление последовательности символов — указатель плюс длина, без null-terminator и без владения памятью. Основной сценарий — параметры функций без лишних аллокаций.A non-owning, immutable view of a character sequence — a pointer plus a length, no null terminator, no memory ownership. The primary use case is function parameters without unnecessary allocations.

часто используетсяcommonly used Library Fundamentals TS v1 частая причина use-after-freea common source of use-after-free

Что этоWhat it is

std::string_view — это буквально пара «указатель на первый символ + длина», не владеющая данными, на которые указывает. Она не аллоцирует, не копирует, не хранит null-terminator — просто указывает на существующие символы, где бы они ни лежали: в std::string, в строковом литерале, в char[] буфере. Основное применение — параметры функций, принимающие и const char*, и std::string, и подстроку без единой лишней аллокации.std::string_view is literally a pair of "pointer to the first character + length," owning none of the data it points to. It doesn't allocate, doesn't copy, doesn't store a null terminator — it simply points at existing characters, wherever they live: inside a std::string, a string literal, a char[] buffer. Its primary use is function parameters that accept a const char*, a std::string, or a substring alike, without a single extra allocation.

до C++17 — const std::string& всё равно аллоцируетbefore C++17 — const std::string& still allocates
void log(const std::string& msg);

log("hello");
// "hello" — const char*, неявно создаётся временный std::string,// "hello" is a const char*, an implicit temporary std::string is built,
// который аллоцирует память (если строка не влезает в SSO-буфер)// which allocates memory (unless the string fits the SSO buffer)
// только ради того, чтобы тут же быть уничтоженным// only to be destroyed right after
C++17 — std::string_view без аллокацииC++17 — std::string_view with no allocation
void log(std::string_view msg);

log("hello");       // указывает прямо на литерал, без аллокации// points straight at the literal, no allocation
std::string s = build();
log(s);              // указывает на буфер s.data(), тоже без копирования// points at s.data()'s buffer, also without copying
log(std::string_view(s).substr(0, 3));  // substr на view — тоже без аллокации// substr on a view — also with no allocation

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

До C++17 не существовало способа принять «что угодно похожее на строку» без выбора между перегрузками и лишними аллокациями:Before C++17 there was no way to accept "anything string-like" without a choice between overloads and unnecessary allocations:

01

const std::string& требует существования std::string, даже временногоconst std::string& requires a std::string to exist, even a temporary one

Функция с параметром const std::string&, вызванная с const char*, всё равно провоцирует неявное построение временного std::string — со всеми накладными расходами SSO-проверки и, при достаточно длинной строке, реальной аллокации в куче, хотя сама функция может лишь читать символы, ничего не изменяя и никуда не сохраняя.A function with a const std::string& parameter, called with a const char*, still triggers an implicit construction of a temporary std::string — with all the overhead of the SSO check and, for a sufficiently long string, an actual heap allocation, even though the function might only read the characters, changing and storing nothing.

02

Перегрузки под каждый строковый тип не масштабируютсяOverloads for every string type don't scale

Обходной путь — писать отдельные перегрузки под const char* и под const std::string& — удваивает объём кода на каждую такую функцию и всё равно не покрывает произвольную подстроку без выделения отдельного std::string под неё.The workaround — writing separate overloads for const char* and const std::string& — doubles the code for every such function and still doesn't cover an arbitrary substring without carving out a separate std::string for it.

03

string_view закрывает это одним типом параметраstring_view closes this with a single parameter type

std::string_view неявно конструируется и из const char*, и из std::string, и из подстроки — без копирования данных в обоих случаях. Одна сигнатура функции покрывает все три источника строки одновременно.std::string_view constructs implicitly from a const char*, from a std::string, and from a substring alike — without copying the data in any of these cases. One function signature covers all three string sources at once.

Как и optional/any, string_view пришёл из Library Fundamentals TS v1 (2015) — std::experimental::string_view в коде до C++17 ведёт себя практически идентично финальной версии.Like optional/any, string_view arrived from the Library Fundamentals TS v1 (2015) — std::experimental::string_view in pre-C++17 code behaves nearly identically to the final version.

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

Внутри — буквально два поля: указатель на данные и размер. Ни конструктор, ни деструктор ничего не аллоцируют и не освобождают — string_view не участвует в управлении временем жизни данных вообще, только читает их.Under the hood it's literally two fields: a data pointer and a size. Neither the constructor nor the destructor allocates or frees anything — string_view takes no part in managing the data's lifetime at all, it only reads it.

// концептуально — вся реализация:// conceptually — the entire implementation:
class string_view {
    const char* data_;
    size_t size_;
    // никакого владения, никакого деструктора, который что-то освобождает// no ownership, no destructor that frees anything
};

std::string s = "hello world";
std::string_view sv = s;              // указывает на s.data(), без копии// points at s.data(), no copy
std::string_view sub = sv.substr(0, 5);  // "hello" — та же память, новый размер, всё ещё без копии// "hello" — same memory, new size, still no copy

s += "!";  // если это вызвало реаллокацию буфера s — sv и sub теперь dangling// if this reallocated s's buffer — sv and sub are now dangling

Последняя строка примера — ключевая иллюстрация того, почему string_view требует дисциплины: любая операция, инвалидирующая указатели/итераторы исходной строки (реаллокация при росте std::string, выход временного объекта из скоупа, уничтожение буфера), автоматически делает все существующие string_view на эти данные висячими — без единого предупреждения от компилятора.The last line of the example is the key illustration of why string_view demands discipline: any operation that invalidates the source string's pointers/iterators (a reallocation as a std::string grows, a temporary going out of scope, a buffer being destroyed) automatically leaves every existing string_view into that data dangling — without a single warning from the compiler.

ГраблиGotchas

Dangling — string_view не владеет даннымиDangling — string_view doesn't own the data

Классическая ошибка: std::string_view sv = someFunc();, где someFunc() возвращает временный std::string по значению. Временный объект уничтожается в конце полного выражения (в момент завершения инициализации sv), а sv продолжает указывать на уже освобождённую память — use-after-free при первом же обращении к sv. string_view не продлевает время жизни временного объекта, в отличие от const T&.The classic bug: std::string_view sv = someFunc();, where someFunc() returns a temporary std::string by value. The temporary is destroyed at the end of the full expression (right after sv's initialization completes), and sv keeps pointing at already-freed memory — use-after-free on the very first access to sv. string_view does not extend a temporary's lifetime the way a const T& would.

Нет null-terminator — data() нельзя напрямую передавать в C APINo null terminator — data() can't be passed directly to a C API

string_view::data() возвращает указатель на первый символ, но не гарантирует наличие \0 сразу после последнего символа диапазона — в отличие от std::string::c_str(). Передать sv.data() напрямую в функцию, ожидающую null-terminated const char* (большинство C API), может прочитать за пределы видимого диапазона данных. Решение — либо явно собрать std::string через substr/конструктор из view, либо убедиться, что исходная строка гарантированно null-terminated и view покрывает её целиком.string_view::data() returns a pointer to the first character but does not guarantee a \0 right after the range's last character — unlike std::string::c_str(). Passing sv.data() directly to a function expecting a null-terminated const char* (most C APIs) can read past the visible data range. The fix is either to explicitly build a std::string via a constructor/substr from the view, or to make sure the source string is guaranteed null-terminated and the view covers it entirely.

Неизменяемость — string_view никогда не модифицирует то, на что указываетImmutability — string_view never modifies what it points to

В отличие от std::span<char> (C++20), string_view всегда read-only по дизайну — методы вроде substr, remove_prefix, remove_suffix лишь меняют, на какую часть исходных данных указывает сам view, не трогая символы. Если нужна изменяемая невладеющая последовательность символов, string_view для этого не подходит в принципе — нужен либо std::span<char>, либо прямая работа с указателем/итераторами std::string.Unlike std::span<char> (C++20), string_view is always read-only by design — methods like substr, remove_prefix, remove_suffix only change which portion of the source data the view itself points at, never touching the characters. If a mutable non-owning character sequence is needed, string_view simply isn't the right tool — you'd need either std::span<char> or direct work with a std::string's pointer/iterators.