std::string and string_view

C++ · Essentials · 12 min

Text processing is everywhere, and choosing between string, string_view and C strings affects both safety and speed.

std::string (header <string>) owns a mutable sequence of chars and manages its own memory. It behaves like a value: copying a string copies its characters, and == compares contents.

The common operations:

  • Concatenate with + and +=; size() and empty(); index with s[i].
  • s.substr(pos, count) copies out a piece; s.find("x") returns a position or std::string::npos if not found.
  • C++20 adds starts_with and ends_with; C++23 adds contains.
  • Convert with std::to_string(42) and std::stoi("42") / std::stod (these throw on bad input).

A string literal like "abc" is not a std::string; it's a const char[4] array inherited from C, with a hidden terminating '\0'. So "a" + "b" doesn't compile (it tries to add two pointers), while std::string{"a"} + "b" works. Short strings (typically up to 15 characters) are stored inside the string object itself, the small string optimization, so they don't allocate.

std::string_view (C++17, header <string_view>) is a non-owning view: just a pointer and a length. It can refer to a std::string, a literal or part of a buffer without copying, which makes std::string_view the best parameter type for functions that only read text. Its substr is free.

The flip side: a view does not keep the text alive. Never return a string_view into a local string, and never store one that outlives the string it points to.

char holds bytes, not Unicode characters; UTF-8 text works fine in a std::string, but size() counts bytes.

Example

#include <cctype>
#include <iostream>
#include <string>
#include <string_view>

bool is_header(std::string_view line) {      // cheap read-only view of any text
    return line.starts_with("#");            // C++20
}

int main() {
    std::string name = "Ada";
    std::string greeting = "Hello, " + name + "!";   // needs one std::string operand
    greeting += " Welcome.";

    std::cout << greeting.size() << '\n';             // 20
    std::cout << greeting.substr(7, 3) << '\n';       // Ada (pos, count)

    auto pos = greeting.find("Welcome");
    if (pos != std::string::npos) std::cout << "found at " << pos << '\n';  // 12

    int n = std::stoi("42");                          // string -> int
    std::string s = std::to_string(n * 2);            // int -> string "84"
    std::cout << s << ' ' << (name == "Ada") << ' ' << is_header("# title") << '\n';  // 84 1 1

    for (char& c : name) c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
    std::cout << name << '\n';                        // ADA
}

Remember

  • std::string owns its text; std::string_view only looks at someone else's
  • Take read-only text parameters as std::string_view
  • find returns std::string::npos when nothing matches

Watch out for

  • Returning a std::string_view that points into a local or temporary std::string.

Check yourself

  1. Why doesn't `auto s = "Hello, " + "world";` compile?
  2. Which parameter type lets a read-only function accept a std::string, a literal, or part of a buffer without copying?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: en.cppreference.com/w/cpp/string/basic_string