Coming Soon

This lesson is currently being developed

Introduction to std::string_view

Learn to work with text strings using the standard library.

Constants and Strings
Chapter
Beginner
Difficulty
40min
Estimated Time

What to Expect

Comprehensive explanations with practical examples

Interactive coding exercises to practice concepts

Knowledge quiz to test your understanding

Step-by-step guidance for beginners

Development Status

In Progress

Content is being carefully crafted to provide the best learning experience

Preview

Early Preview Content

This content is still being developed and may change before publication.

5.8 — Introduction to std::string_view

In this lesson, you'll learn about std::string_view, a lightweight way to work with string data that doesn't own the memory. You'll understand when to use it, how it differs from std::string, and why it can improve performance.

What is std::string_view?

std::string_view is a lightweight, non-owning reference to a sequence of characters. Introduced in C++17, it provides a way to view string data without creating copies or managing memory.

Think of std::string_view as a window that lets you look at string data that exists somewhere else - it doesn't own the data, but it can read and analyze it efficiently.

Key differences: std::string vs std::string_view

Feature std::string std::string_view
Memory ownership Owns its data Views existing data
Memory allocation May allocate memory No allocation
Modification Can modify content Read-only view
Copying cost Expensive (copies data) Cheap (copies view)
Lifetime Manages its own lifetime Depends on viewed data

Including std::string_view

std::string_view requires C++17 and the <string_view> header:

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

int main()
{
    std::string_view message = "Hello, World!";
    std::cout << message << std::endl;
    
    return 0;
}

Output:

Hello, World!

Creating std::string_view objects

From string literals

#include <iostream>
#include <string_view>

int main()
{
    // Create string_view from string literals
    std::string_view greeting = "Hello";
    std::string_view message = "Welcome to C++!";
    
    std::cout << "Greeting: " << greeting << std::endl;
    std::cout << "Message: " << message << std::endl;
    std::cout << "Greeting length: " << greeting.length() << std::endl;
    std::cout << "Message length: " << message.length() << std::endl;
    
    return 0;
}

Output:

Greeting: Hello
Message: Welcome to C++!
Greeting length: 5
Message length: 15

From std::string objects

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

int main()
{
    std::string str = "Programming in C++";
    std::string_view view = str;  // Create view from string
    
    std::cout << "Original string: " << str << std::endl;
    std::cout << "String view: " << view << std::endl;
    
    // Both refer to the same data
    std::cout << "Same data? " << (view.data() == str.data()) << std::endl;
    
    return 0;
}

Output:

Original string: Programming in C++
String view: Programming in C++
Same data? 1

From character arrays

#include <iostream>
#include <string_view>

int main()
{
    char buffer[] = "Hello, World!";
    std::string_view view(buffer);
    
    std::cout << "Buffer: " << buffer << std::endl;
    std::cout << "View: " << view << std::endl;
    std::cout << "View length: " << view.length() << std::endl;
    
    // Create partial view
    std::string_view partial(buffer, 5);  // First 5 characters
    std::cout << "Partial view: " << partial << std::endl;
    
    return 0;
}

Output:

Buffer: Hello, World!
View: Hello, World!
View length: 13
Partial view: Hello

Basic string_view operations

Accessing characters

#include <iostream>
#include <string_view>

int main()
{
    std::string_view text = "Programming";
    
    std::cout << "Text: " << text << std::endl;
    std::cout << "First character: " << text[0] << std::endl;
    std::cout << "Last character: " << text[text.length() - 1] << std::endl;
    std::cout << "Third character: " << text.at(2) << std::endl;
    
    // Front and back methods
    std::cout << "Front: " << text.front() << std::endl;
    std::cout << "Back: " << text.back() << std::endl;
    
    // Print all characters
    std::cout << "All characters: ";
    for (size_t i = 0; i < text.length(); ++i)
    {
        std::cout << text[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

Output:

Text: Programming
First character: P
Last character: g
Third character: o
Front: P
Back: g
All characters: P r o g r a m m i n g 

Size and empty checks

#include <iostream>
#include <string_view>

int main()
{
    std::string_view empty;
    std::string_view text = "Hello, World!";
    
    std::cout << "Empty view:" << std::endl;
    std::cout << "  Content: '" << empty << "'" << std::endl;
    std::cout << "  Length: " << empty.length() << std::endl;
    std::cout << "  Size: " << empty.size() << std::endl;
    std::cout << "  Empty: " << empty.empty() << std::endl;
    
    std::cout << "\nText view:" << std::endl;
    std::cout << "  Content: " << text << std::endl;
    std::cout << "  Length: " << text.length() << std::endl;
    std::cout << "  Size: " << text.size() << std::endl;
    std::cout << "  Empty: " << text.empty() << std::endl;
    
    return 0;
}

Output:

Empty view:
  Content: ''
  Length: 0
  Size: 0
  Empty: 1

Text view:
  Content: Hello, World!
  Length: 13
  Size: 13
  Empty: 0

Substring operations

#include <iostream>
#include <string_view>

int main()
{
    std::string_view text = "The quick brown fox";
    
    std::cout << "Original: " << text << std::endl;
    
    // Extract substrings using substr()
    std::string_view first_word = text.substr(0, 3);      // "The"
    std::string_view second_word = text.substr(4, 5);     // "quick"
    std::string_view from_brown = text.substr(10);        // "brown fox"
    
    std::cout << "First word: " << first_word << std::endl;
    std::cout << "Second word: " << second_word << std::endl;
    std::cout << "From 'brown': " << from_brown << std::endl;
    
    // Remove prefix and suffix
    std::string_view no_prefix = text;
    no_prefix.remove_prefix(4);    // Remove "The "
    std::cout << "After removing prefix: " << no_prefix << std::endl;
    
    std::string_view no_suffix = no_prefix;
    no_suffix.remove_suffix(4);   // Remove " fox"
    std::cout << "After removing suffix: " << no_suffix << std::endl;
    
    return 0;
}

Output:

Original: The quick brown fox
First word: The
Second word: quick
From 'brown': brown fox
After removing prefix: quick brown fox
After removing suffix: quick brown

Finding and searching

#include <iostream>
#include <string_view>

int main()
{
    std::string_view text = "The quick brown fox jumps over the lazy dog";
    
    std::cout << "Text: " << text << std::endl;
    
    // Find substrings
    size_t pos1 = text.find("fox");
    size_t pos2 = text.find("cat");
    size_t pos3 = text.find("the");        // First occurrence
    size_t pos4 = text.find("the", 32);    // Starting from position 32
    
    std::cout << "Position of 'fox': ";
    if (pos1 != std::string_view::npos)
        std::cout << pos1 << std::endl;
    else
        std::cout << "not found" << std::endl;
        
    std::cout << "Position of 'cat': ";
    if (pos2 != std::string_view::npos)
        std::cout << pos2 << std::endl;
    else
        std::cout << "not found" << std::endl;
        
    std::cout << "First 'the' at: " << pos3 << std::endl;
    std::cout << "Next 'the' after pos 32: " << pos4 << std::endl;
    
    // Find characters
    size_t char_pos = text.find('q');
    std::cout << "First 'q' at position: " << char_pos << std::endl;
    
    // Find first/last of character sets
    size_t vowel_pos = text.find_first_of("aeiou");
    size_t consonant_pos = text.find_first_not_of("The ");
    
    std::cout << "First vowel at: " << vowel_pos << std::endl;
    std::cout << "First non-'The ' character at: " << consonant_pos << std::endl;
    
    return 0;
}

Output:

Text: The quick brown fox jumps over the lazy dog
Position of 'fox': 16
Position of 'cat': not found
First 'the' at: 0
Next 'the' after pos 32: 31
First 'q' at position: 4
First vowel at: 2
First non-'The ' character at: 4

Comparing string_views

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

int main()
{
    std::string_view view1 = "apple";
    std::string_view view2 = "banana";
    std::string_view view3 = "apple";
    
    std::cout << "view1: " << view1 << std::endl;
    std::cout << "view2: " << view2 << std::endl;
    std::cout << "view3: " << view3 << std::endl;
    
    // Equality comparisons
    std::cout << "\nEquality:" << std::endl;
    std::cout << "view1 == view2: " << (view1 == view2) << std::endl;
    std::cout << "view1 == view3: " << (view1 == view3) << std::endl;
    std::cout << "view1 != view2: " << (view1 != view2) << std::endl;
    
    // Lexicographic comparisons
    std::cout << "\nLexicographic:" << std::endl;
    std::cout << "view1 < view2: " << (view1 < view2) << std::endl;
    std::cout << "view2 > view1: " << (view2 > view1) << std::endl;
    
    // Compare with string literals
    std::cout << "\nWith literals:" << std::endl;
    std::cout << "view1 == \"apple\": " << (view1 == "apple") << std::endl;
    
    // Compare with std::string
    std::string str = "apple";
    std::cout << "view1 == str: " << (view1 == str) << std::endl;
    
    return 0;
}

Output:

view1: apple
view2: banana
view3: apple

Equality:
view1 == view2: 0
view1 == view3: 1
view1 != view2: 1

Lexicographic:
view1 < view2: 1
view2 > view1: 1

With literals:
view1 == "apple": 1
view1 == str: 1

Performance benefits

Function parameters

Using string_view as function parameters can improve performance by avoiding unnecessary copies:

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

// Function using string_view (efficient)
void printStringInfo(std::string_view text)
{
    std::cout << "Text: " << text << std::endl;
    std::cout << "Length: " << text.length() << std::endl;
    std::cout << "First char: " << text.front() << std::endl;
    std::cout << "Last char: " << text.back() << std::endl;
}

// Function using const string& (still efficient, but less flexible)
void printStringInfoRef(const std::string& text)
{
    std::cout << "Text (ref): " << text << std::endl;
    std::cout << "Length: " << text.length() << std::endl;
}

int main()
{
    std::string str = "Hello, World!";
    const char* cstr = "Hello, C++!";
    
    std::cout << "Using string_view parameter:" << std::endl;
    printStringInfo(str);        // Works with std::string
    printStringInfo(cstr);       // Works with C-string
    printStringInfo("Literal");  // Works with string literal
    
    std::cout << "\nUsing const string& parameter:" << std::endl;
    printStringInfoRef(str);     // Works with std::string
    // printStringInfoRef(cstr); // Would create temporary std::string
    // printStringInfoRef("Literal"); // Would create temporary std::string
    
    return 0;
}

Output:

Using string_view parameter:
Text: Hello, World!
Length: 13
First char: H
Last char: !
Text: Hello, C++!
Length: 11
First char: H
Last char: !
Text: Literal
Length: 7
First char: L
Last char: l

Using const string& parameter:
Text (ref): Hello, World!
Length: 13

Avoiding unnecessary copies

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

// Function that processes string data without modifying it
std::string_view getFileExtension(std::string_view filename)
{
    size_t dot_pos = filename.find_last_of('.');
    if (dot_pos != std::string_view::npos)
    {
        return filename.substr(dot_pos + 1);  // Return view of extension
    }
    return {};  // Empty string_view if no extension
}

std::string_view getFilename(std::string_view path)
{
    size_t slash_pos = path.find_last_of("/\\");
    if (slash_pos != std::string_view::npos)
    {
        return path.substr(slash_pos + 1);
    }
    return path;  // No path separators found
}

int main()
{
    std::string filepath = "/home/user/documents/report.txt";
    
    std::cout << "Full path: " << filepath << std::endl;
    
    std::string_view filename = getFilename(filepath);
    std::string_view extension = getFileExtension(filename);
    
    std::cout << "Filename: " << filename << std::endl;
    std::cout << "Extension: " << extension << std::endl;
    
    // These operations are efficient - no string copying involved!
    
    return 0;
}

Output:

Full path: /home/user/documents/report.txt
Filename: report.txt
Extension: txt

Common use cases

Text parsing

#include <iostream>
#include <string_view>
#include <vector>

// Simple word splitter using string_view
std::vector<std::string_view> splitWords(std::string_view text)
{
    std::vector<std::string_view> words;
    size_t start = 0;
    
    while (start < text.length())
    {
        // Skip whitespace
        while (start < text.length() && text[start] == ' ')
            ++start;
            
        if (start >= text.length())
            break;
            
        // Find end of word
        size_t end = start;
        while (end < text.length() && text[end] != ' ')
            ++end;
            
        // Add word to vector
        words.push_back(text.substr(start, end - start));
        start = end;
    }
    
    return words;
}

int main()
{
    std::string text = "The quick brown fox jumps";
    
    std::cout << "Original text: " << text << std::endl;
    
    std::vector<std::string_view> words = splitWords(text);
    
    std::cout << "Words (" << words.size() << "):" << std::endl;
    for (size_t i = 0; i < words.size(); ++i)
    {
        std::cout << "  " << i + 1 << ": '" << words[i] << "'" << std::endl;
    }
    
    return 0;
}

Output:

Original text: The quick brown fox jumps
Words (5):
  1: 'The'
  2: 'quick'
  3: 'brown'
  4: 'fox'
  5: 'jumps'

Configuration parsing

#include <iostream>
#include <string_view>

struct ConfigValue
{
    std::string_view key;
    std::string_view value;
};

ConfigValue parseConfigLine(std::string_view line)
{
    // Find the '=' separator
    size_t equals_pos = line.find('=');
    if (equals_pos == std::string_view::npos)
    {
        return {"", ""};  // Invalid line
    }
    
    std::string_view key = line.substr(0, equals_pos);
    std::string_view value = line.substr(equals_pos + 1);
    
    // Trim whitespace (simplified)
    while (!key.empty() && key.back() == ' ')
        key.remove_suffix(1);
    while (!value.empty() && value.front() == ' ')
        value.remove_prefix(1);
        
    return {key, value};
}

int main()
{
    std::string config_data = 
        "username=john_doe\n"
        "password=secret123\n"
        "port=8080\n"
        "debug=true\n";
    
    std::cout << "Configuration data:" << std::endl;
    std::cout << config_data << std::endl;
    
    // Parse each line
    std::string_view data_view = config_data;
    size_t pos = 0;
    
    while (pos < data_view.length())
    {
        size_t newline = data_view.find('\n', pos);
        if (newline == std::string_view::npos)
            newline = data_view.length();
            
        std::string_view line = data_view.substr(pos, newline - pos);
        ConfigValue config = parseConfigLine(line);
        
        if (!config.key.empty())
        {
            std::cout << "Key: '" << config.key 
                      << "', Value: '" << config.value << "'" << std::endl;
        }
        
        pos = newline + 1;
    }
    
    return 0;
}

Output:

Configuration data:
username=john_doe
password=secret123
port=8080
debug=true

Key: 'username', Value: 'john_doe'
Key: 'password', Value: 'secret123'
Key: 'port', Value: '8080'
Key: 'debug', Value: 'true'

Important considerations

Lifetime management

Be careful about the lifetime of the data that string_view references:

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

std::string_view dangerousFunction()
{
    std::string local = "This is dangerous!";
    return local;  // ❌ DANGER: Returning view to local variable
                   // The string will be destroyed when function ends
}

std::string_view safeFunction()
{
    return "This is safe";  // ✅ SAFE: String literal has static lifetime
}

int main()
{
    std::string_view safe_view = safeFunction();
    std::cout << "Safe view: " << safe_view << std::endl;
    
    // Be careful with this pattern:
    std::string_view dangerous_view = dangerousFunction();
    // Using dangerous_view here is undefined behavior!
    
    return 0;
}

Read-only nature

Remember that string_view is read-only:

#include <iostream>
#include <string_view>

int main()
{
    std::string_view text = "Hello, World!";
    
    std::cout << "Original: " << text << std::endl;
    
    // These operations are read-only and safe:
    std::cout << "Length: " << text.length() << std::endl;
    std::cout << "First char: " << text[0] << std::endl;
    std::cout << "Substring: " << text.substr(0, 5) << std::endl;
    
    // This would not compile - string_view is read-only:
    // text[0] = 'h';  // ❌ Error: cannot modify through string_view
    
    return 0;
}

Summary

std::string_view is a powerful tool for efficient string handling:

  • Lightweight: No memory allocation, just a view of existing data
  • Efficient: Avoids unnecessary string copies
  • Flexible: Works with string literals, std::string, and character arrays
  • Read-only: Provides a non-modifying view of string data
  • C++17 feature: Requires modern C++ compiler support

Key benefits:

  • Better performance when passing strings to functions
  • Efficient substring operations
  • Unified interface for different string types
  • Zero-copy string operations

Important considerations:

  • Be careful about lifetime of viewed data
  • Read-only access only
  • Requires C++17 or later

std::string_view is particularly useful for function parameters, parsing operations, and any situation where you need to examine string data without modifying it.

Quiz

  1. What C++ standard introduced std::string_view?
  2. What's the main advantage of string_view over std::string for function parameters?
  3. Can you modify the content of a string through a string_view?
  4. What happens if you create a string_view from a local std::string and return it from a function?
  5. What's the difference between remove_prefix() and substr() for getting a substring?

Practice exercises

Try these string_view exercises:

  1. Write a function that takes a string_view and counts the number of words in it
  2. Create a simple CSV parser using string_view that splits a line into fields
  3. Write a function that checks if a filename (passed as string_view) has a specific extension
  4. Implement a simple text trimming function that removes leading and trailing whitespace using string_view operations

Continue Learning

Explore other available lessons while this one is being prepared.

View Course

Explore More Courses

Discover other available courses while this lesson is being prepared.

Browse Courses

Lesson Discussion

Share your thoughts and questions

💬

No comments yet. Be the first to share your thoughts!

Sign in to join the discussion