Differences between std::string_view and std::span

Question | Mar 4, 2020 | nwheeler 

ir-md-block-image ir-md-block-image-bottom-line ir-md-block-image-w-70

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

char buffer with views

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());

Which one (std::string_view or std::span<const char>) do you think can satisfy our requirement as Code? Select below (check Explanations for details):