Overview

The motivations behind std::string_view and std::span are similar. The string_view and span are objects that refer to a contiguous sequence of elements starting at position zero and provide standard container operations. Both types are lightweight easy-to-copy objects comprising a pointer and a size member. Conceptually, they are non-owning views of an array (or contiguous sequence) that provide the rich standard interface.

As for the differences between the two, the std::span< T > is a general-purpose array view template, whereas the std::string_view is a more specialized view on a char sequence or string.

These are the brief descriptions of notable differences between the two:

1. span is a template, but string_view is not

The span is a template that works with any user-defined or primitive type, whereas the string_view is specifically a view on a contiguous char array. Superficially, a string_view is equivalent to span< char >:

char buff[] = "Hello World"; auto sp = std::span<char>(&buff[0], 5); auto sv = std::string_view(&buff[6], 5); for(auto c : sp) std::cout << c; //Hello std::cout << " "; for(auto c : sv) std::cout << c; //World //Above code prints: Hello World

But string_view is more than a span< char >, read on the next two differences to understand more.

2. string_view is a read-only view

One distinctive feature of string_view is that it is a read-only view. Therefore, we cannot change an underlying array through a string_view, though we can change an underlying array through a span:

char str[] = "hello"; //Change str to uppercase through span auto sp = std::span<char>(str,strlen(str)); std::transform(sp.begin(), sp.end(), sp.begin(), [](char c) { return std::toupper(c); }); //str is now HELLO //Back to lowercase via string_view is not possible. auto sv = std::string_view(str); std::transform(sv.begin(), sv.end(), sv.begin(), [](char c) { return std::tolower(c); }); //ERROR!

If string_view is overtly read-only, the span is flexible enough to be turned into a read-only view through span< const T >. Therefore, a string_view is closer to a span< const char >. But wait, before you question the rationality behind string_view, there is one more important difference that warrants the existence of the string_view.

3. string_view supports std::string-like operations

The span has several sequential container operations, e.g., front, back, begin, end, operator[]. However, string_view has several more std::string-like methods, e.g., substr, find, compare, overloaded comparison operators (e.g., ==,<,>). Therefore, the string_view can eliminate the need to create an std::string object in some cases where the underlying storage does not matter.

Considering the similarities and differences between the two, we can say that the std::string_view is to std::string what std::span< const char > is to std::vector< char >.

A Question

Let's look at an example that throws more light on this subject. Suppose we have a char array that stores many fixed-length 3-letter alphabetical codes. This char array, buffer, stays in the memory for the lifetime of the process:

char buffer[] = "CAB" "DEF" "GAG" "DAD" "RAP" /*more*/; //A buffer with many codes

We split the above array into many objects of a type Code. The Code can be a lightweight array view object. However, we want to use the Code as a key in an std::map to store some data against it, as follows:

struct SomeData { //... }; std::map<Code, SomeData> codesData;

We are thinking of to declare Code an alias of either string_view or span< const char >:

using Code = std::string_view; // OR using Code = std::span<const char>; /*Data can be loaded into the map for each Code as: */ for(size_t i=0; i < sizeof buffer; i=i+3) codesData.emplace(Code(&buffer[i], 3), SomeData());