Skip to Content
Course content

74: std::string_view for Efficient Views

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I want to show you a piece of code that looks perfectly reasonable at first glance, but it's actually a ticking time bomb. This is a classic mistake I see developers make when they first start using std::string_view because they treat it like a "lightweight string" instead of what it actually is: a window into someone else's data.

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

std::string_view get_username_from_header(std::string_view header) {
    // Let's say the header is "User: Alice" and we want just "Alice"
    size_t pos = header.find(": ");
    if (pos == std::string_view::npos) return "";

    // We create a new string to "clean up" the result for the caller
    std::string cleaned = std::string(header.substr(pos + 2));
    
    // Oops! Returning a view of a local variable
    return std::string_view(cleaned); 
}

int main() {
    std::string header = "User: Alice";
    std::string_view user = get_username_from_header(header);
    std::cout << "Welcome, " << user << "!" << std::endl; // Crash or garbage output
    return 0;
}

The Danger of the Temporary String

If you run this, you might get "Welcome, Alice!", or you might get a crash, or you might get a string of random Chinese characters. It's a classic use-after-free bug. Here is what's happening: inside get_username_from_header, I created a std::string called cleaned. That variable lives on the stack. When the function returns, cleaned is destroyed, and its memory is freed.

A std::string_view doesn't own the characters it points to; it's just a pointer and a length. By returning a view of cleaned, you're handing the main function a pointer to memory that has already been reclaimed by the system. I've spent way too many hours debugging "heisenbugs" like this—where the code works on your machine but fails in production because the memory wasn't overwritten quite yet.

Switching to Non-Owning Slices

The beauty of std::string_view is that it allows us to avoid allocations entirely. The mistake in the code above was creating a temporary std::string. If we just operate on the view we were already given, we don't need to allocate anything, and the data remains valid as long as the original header string in main exists.

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

std::string_view get_username_from_header(std::string_view header) {
    size_t pos = header.find(": ");
    if (pos == std::string_view::npos) return "";

    // Just return a slice of the original view.
    // No new string is created, no memory is allocated.
    return header.substr(pos + 2);
}

int main() {
    std::string header = "User: Alice";
    // The view now points directly into the 'header' string in main.
    std::string_view user = get_username_from_header(header);
    std::cout << "Welcome, " << user << "!" << std::endl; 
    return 0;
}

Now, the code is not only safe but significantly faster. We aren't calling the heap allocator to create a temporary string, and we aren't copying characters. We are simply sliding a window over the existing buffer.

When to Use View vs. Value

I generally follow a simple rule of thumb: use std::string_view for function arguments when the function only needs to read the string. It allows the function to accept both std::string and C-style string literals without forcing a conversion or an allocation.

However, be extremely cautious when returning a string_view. You must be absolutely certain that the data being viewed is owned by someone else who will outlive the view. If your function creates or modifies a string, return a std::string. If your function just finds a piece of an existing string, std::string_view is your best friend.

  • std::string: Owns the memory. Use when you need to modify the text or ensure it stays alive.
  • std::string_view: Borrows the memory. Use for read-only access to avoid expensive copies.



📋 Practical Task

Building a Zero-Allocation Log Parser

You are tasked with writing a high-performance log parser. You have a large log entry as a std::string, and you need to extract the Timestamp, Log Level, and Message without creating any new std::string objects (which would cause heap allocations).

Requirements:

  • Create a struct LogEntry that contains three std::string_view members: timestamp, level, and message.
  • Write a function LogEntry parse_log_line(std::string_view line).
  • The log format is: [YYYY-MM-DD HH:MM:SS] [LEVEL] Message text here.
  • Use find() and substr() to slice the string_view.
  • In your main function, create a std::string` containing a sample log line, call your parser, and print the extracted fields to verify they are correct.

Hint: Remember that string_view::substr(pos, count) takes the starting position and the length of the slice, not the ending position.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.