string_view and Non-Owning Views

A std::string_view is a tiny window — just a pointer and a length — over characters that live somewhere else. It lets you slice strings and accept text parameters with zero copying . The catch is that it doesn't own its data, so it can dangle. By the end you'll use views to make string code faster, and know exactly when they are safe.

Learn string_view and Non-Owning Views in our free C++ course — an interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

A std::string is like photocopying a page of a book so you can keep your own copy. A std::string_view is like sliding a cardboard frame over part of the original page — you can read the words inside the frame, and you can move or shrink the frame instantly, but you never copy anything. The danger is obvious: if someone closes the book (destroys the string) or reshuffles the pages (reallocates the buffer) while your frame is still resting on it, the frame now sits over nothing. The frame only works while the page underneath stays put.

1. Owning vs Viewing

A std::string owns its characters: constructing one allocates memory and copies the data in. A std::string_view owns nothing — it stores only a pointer to the first character and a length, describing a window into characters that already exist. That makes copying a view trivially cheap (so you pass it by value), and it makes a substring of a view free : substr , remove_prefix , and remove_suffix just move the window's edges without touching the data.

2. string_view as a Function Parameter

The classic read-only string parameter was const std::string& . But when a caller passes a string literal or a char array, that forces the construction of a temporary std::string — an allocation and a copy just to call the function. A std::string_view parameter binds directly to std::string , literals, and char arrays alike, with no allocation, while still offering size() , substr() and the other read operations. For read-only text, string_view is the modern default.

Your turn. Complete firstWord so it returns a view of the text before the first space — no copying allowed:

These lines should make a string, view it, slice the view, and print the slice. Put them in the right order:

Open main (D), create the owning string (B), make a view of it (A), print a zero-copy slice (C), then return 0; (E) and close the brace (F). The owner must exist before the view.

3. The Dangling Danger

Because a view doesn't own its data, it is only valid while that data lives. Three patterns are undefined behaviour: returning a view of a local string (the local dies on return), storing a view of a temporary (the temporary dies at the end of the statement), and mutating the owner while a view of it exists (appending can reallocate the buffer, invalidating the view's pointer). The rule of thumb: a string_view must never outlive the data it points at, and the owner must not be reallocated underneath it.

No null terminator: a string_view can point at a slice in the middle of a buffer, so there is no guaranteed trailing \0 . Never hand view.data() to a C API that expects a null-terminated string — build a std::string from the view first and use its .c_str() .

4. std::span — The Array Analogue

std::span<T> (C++20) takes the exact same idea — a non-owning pointer plus length — and applies it to any contiguous sequence: the elements of a std::vector , a std::array , or a raw C buffer. A function taking span<const int> accepts all of them without copying, just as string_view does for characters. The same caveat applies: a span borrows, so it must not outlive its data, and subspan only adjusts the window.

Borrow by default, own when you must outlive the source. Use a string_view parameter for read-only text that you only need during the call. The moment you need to store the characters past the lifetime of the source — in a member, a container, or a returned value — copy them into an owning std::string .

Conversion goes both ways and is explicit in the dangerous direction: a std::string converts to a string_view implicitly (cheap, borrowing), but turning a string_view back into an owning string requires you to write std::string(sv) — a deliberate copy that makes the ownership transfer visible.

Predict the result before revealing the answer.

No — local is destroyed when f returns, so the view dangles. Return an owning std::string instead.

Zero — substr on a view just shifts the pointer and length; no characters are copied.

The append may reallocate s , leaving v pointing at freed memory — undefined behaviour. Don't mutate the owner while a view is alive.

Write countWords(string_view) that counts space-separated words by walking the view with find and remove_prefix — never building a std::string , never copying.

Practice quiz

What does a std::string_view actually store?

  • A pointer to char data plus a length
  • A full owning copy of the characters
  • A reference count and a buffer
  • A null-terminated C string only

Answer: A pointer to char data plus a length. A string_view is just a pointer and a length — a lightweight, non-owning window over character data that lives elsewhere.

Why is taking a substring of a string_view cheap?

  • It uses move semantics on the buffer
  • It compresses the data
  • It only adjusts the pointer and length — no characters are copied
  • It caches the result

Answer: It only adjusts the pointer and length — no characters are copied. substr on a view returns another view by shifting the pointer and shrinking the length; no allocation or copy happens.

Which parameter type lets a function accept both std::string and string literals without copying?

  • std::string by value
  • std::string_view
  • char*
  • const std::string&

Answer: std::string_view. A std::string_view parameter binds to std::string, string literals, and char arrays alike, all without an allocation or copy.

What is the main danger of std::string_view?

  • It is always slower than std::string
  • It cannot be passed to functions
  • It copies too much memory
  • It can dangle if the data it views is destroyed before the view

Answer: It can dangle if the data it views is destroyed before the view. A view does not own its data, so if the underlying string is destroyed or reallocated, the view becomes a dangling reference.

Does std::string_view guarantee its data is null-terminated?

  • No — a view may point into the middle of a larger buffer with no terminator
  • Only if created from a literal
  • Only on Windows
  • Yes, always

Answer: No — a view may point into the middle of a larger buffer with no terminator. A view can reference a slice of a buffer, so there is no guaranteed '\0' at the end; never pass view.data() to C APIs expecting a C string.

Which of these is a SAFE use of string_view?

  • Storing a view of a temporary that has already been destroyed
  • Reading a substring of a string that stays alive for the whole call
  • Viewing a string and then resizing that string
  • Returning a view of a local std::string from a function

Answer: Reading a substring of a string that stays alive for the whole call. A view is safe as long as the data it points at outlives the view and is not reallocated; reading a stable string during a call is fine.

What is the array analogue of string_view in C++20?

  • std::list
  • std::vector
  • std::array
  • std::span<T>

Answer: std::span<T>. std::span<T> is a non-owning view (pointer + length) over a contiguous sequence of any element type — string_view for arrays.

What happens to a std::string_view's contents if you call push_back on the std::string it views?

  • The view updates automatically
  • Nothing can go wrong
  • The string may reallocate, leaving the view dangling
  • The view grows too

Answer: The string may reallocate, leaving the view dangling. Appending can force the string to reallocate its buffer; the old pointer the view held is then invalid — a classic dangling bug.

Why prefer string_view over const std::string& for a read-only parameter?

  • It owns the data more safely
  • It avoids forcing callers with a literal or char array to build a temporary std::string
  • It is null-terminated
  • It allows modifying the string

Answer: It avoids forcing callers with a literal or char array to build a temporary std::string. A const std::string& parameter forces a literal or char array to be converted into a temporary std::string; a string_view binds directly with no allocation.

Which operation is NOT available on std::string_view?

  • push_back
  • substr
  • size
  • remove_prefix

Answer: push_back. A view is read-only and non-owning, so it cannot modify or grow the underlying data; push_back belongs to std::string, not string_view.