Simpleton
Simpleton

Reputation: 703

Why doesn't std::string_view have assign() and clear() methods?

The implementation of these methods seems straightforward to me and they would make usage of std::string and std::string_view more interchangeable. After all, std::string_view has constructors which leave the object in the same state as these methods would. One could workaround the missing methods like this:

std::string s {"abcd"};
std::string_view v {s.c_str()};
std::cout << "ctor:   " << v << std::endl; // "abcd"
v = {s.c_str() + 1, 2};
std::cout << "assign: " << v << std::endl; // "bc"
v = {nullptr}; // or even v = {};
std::cout << "clear:  " << v << std::endl; // ""

So, what are the reasons for not including these two obvious methods in the standard?

UPDATE: One general question in your comments seems to be "What's the point?", so I'll give you some context. I'm parsing a large string with the result being a structure of substrings. That result structure is a natural candidate for string views, so I don't have to copy all those strings, which are even overlapping. Part of the result are maps to string views, so I might need to construct them empty when I get the key and fill them later when I get the value. While parsing I need to keep track of intermediate strings, which involves updating and resetting them. Now they could be replaced by string views as well, and that's how I happened on those missing functions. Of course I could continue using strings or replace them by plain old ptr-ptr or ptr-size pairs, but that's exactly what std::string_view is for, right?

Upvotes: 7

Views: 6062

Answers (6)

Alex Cohn
Alex Cohn

Reputation: 57173

We don't have assign() or clear() methods for string_view, but we luckily have a combination of the two: swap(string_view& other). If you don't need the contents of the original string_view after swap, you can simply forget about it, e.g.

string_view sv {"original text"};
if (want_new) {
  string_view tmp {"new text"};
  sv.swap(tmp);
}

I use this pattern to extend the string_view class, e.g. to wrap a memory mapped file into a string_view. My derived class has a constructor that takes the file name, and a destructor that unmaps and closes the file (error handling skipped for brevity):

class mmmap_string_view: public string_view {
public:
  explicit mmmap_string_view(fs::path path) {
    fd = open(path.c_str(), O_RDONLY);
    auto sz = fs::file_size(path);
    auto *addr = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0);
    string_view tmp {static_cast<const char *>(addr), sz};
    swap(tmp);
  }
  ~mmmap_string_view() {
     munmap(const_cast<char *>(data()), size());
     close(fd);
  }
private:
  int fd = -1;
}

Upvotes: 0

Kai Petzke
Kai Petzke

Reputation: 2934

std::string_views are references to strings elsewhere in memory. But unlike the good (or bad) old C char *, std::string_views rather have a pointer and a size. That's why they can refer to a specific part of some other string, and that's why remove_prefix() and remove_suffix() are meaningful operations on string_views.

However, you don't need an assign() method for std::string_view, as that would be no different from the assignment operator. std::string has an assign() method to care for a lot of cases, that the assignment operator of std::string cannot handle, because it cannot take more than one argument. A few examples:

std::string A { "This is a somewhat lengthy string" };
std::list<char> L { '1', 'A', 'Z' };
std::string B;

B.assign(80, ' ');               // Create an empty line of spaces
B.assign(A, 10, 8);              // Copy out a substring of another string
B.assign(L.begin(), L.end());    // Assign from an STL container

Of these three cases, only the middle one is meaningful with std::string_view. Both the first and last case would require to allocate a string, and that's what std::string_view cannot do.

The middle case, however, can easily be achieved by using std::string_view::substr(). For example:

std::string A { "This is a somewhat lengthy string" };
std::string_view V { "Directly pointing to A C string" };
std::string_view B;

B = std::string_view{A}.substr(10, 8);    // Reference substring of A
B = B.substr(0, 4);                       // Further shorten the reference
B = V.substr(0, 8);                       // Reassign with another reference
B = B.substr(0, 0);                       // "Clear" the string_view

So both clear() and assign() to a std::string_view can be done using the assignment operator instead. This is not true for std::string: The middle assignment B.assign(A, 10, 8); overwrites the already allocated B with data from A. Solutions using the assignment operator like B = A.substr(10, 8); would lead to the creation of a temporary std::string by the substr() call, then followed by an internal freeing of the memory, that was allocated to B before. This is often not efficient, that's why the substring assign method was added to std::string(). On the other hand, std::string_view::substr() does not require to copy any string data, so the assignment B = std::string_view{A}.substr(10, 8); can easily be optimized!

Please note, that the following code, though, is an error and would leave a dangling reference to a temporary substring on the heap:

std::string A { "This is a somewhat lengthy string" };
std::string_view V { A.substr(10, 8) };

So, if you need to set a std::string_view to a substring of a string, always convert to a std::string_view first (via std::string_view(string)) and then perform the required substr() method call on the std::string_view. Of course, make sure, that the original string outlives the newly generated std::string_view, or you end up with dangling references.

Upvotes: 2

huoneusto
huoneusto

Reputation: 1204

string_view could be awesome string interface wrapper for plain C strings (char arrays) when the backing store is not in your control, but you still want to access/modify the contents without the ability -- or need -- to meddle with the backing store (i.e. in contexts where dynamic memory allocation is out of question).

Upvotes: 0

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385104

This is only ever really going to be speculation, but general concensus seems to be that these operations would be middlingly unclear.

Personally I think "clearing a view" makes perfect sense (and let's also not forget that remove_prefix and remove_suffix exist! Though see below...), but I also agree that there are other interpretations, which may be common, which make less sense. Recall that string_view is intended to complement const std::string&, not std::string, and neither of the functions you name is a part of std::string's constant interface.

To be honest, the fact that we need this conversation at all is, itself, probably a good reason to just not have the function in the first place.

From the final proposal for string_view, the following passage is not about assign or clear specifically but does act as a relevant view [lol] into the minds of the committee on this subject:

s/remove_prefix/pop_front/, etc.

In Kona 2012, I proposed a range<> class with pop_front, etc. members that adjusted the bounds of the range. Discussion there indicated that committee members were uncomfortable using the same names for lightweight range operations as container operations. Existing practice doesn't agree on a name for this operation, so I've kept the name used by Google's StringPiece.

This proposal did in fact include a clear(), which was unceremoniously struck off the register in a later, isolated, rationale-starved proposal.

Now, one might argue that the functions could therefore have been provided under different names, but that was never proposed, and it's hard to imagine what alternative names would resolve this problem without being simply bad names for the operations.

Since we can assign a new string_view easily enough, including an empty one, the whole problem is solved by simply not bothering to address it.

Upvotes: 4

Jeff Garrett
Jeff Garrett

Reputation: 7383

The implementation of these methods seems straightforward to me and they would make usage of std::string and std::string_view more interchangeable.

std::string_view is not intended to be a replacement for std::string. It is intended as a replacement for const std::string&. assign and clear are not member functions of const std::string& you can call.

Upvotes: 3

lubgr
lubgr

Reputation: 38267

The std::string interface has a bad reputation due to its blown API, that's why std::string_view is very unlikely to get as many methods as std::string just because it's convenient or makes the two types more interchangeable.

But more important, these types aren't meant to be interchangeable. What does it mean for view on a container of characters to be "cleared"? As clear() is present on all STL containers and does something meaningful, having a std::string_view::clear() would be quite confusing.

In addition, a view on some data is meant for temporary consumption, e.g. a read-only function parameter. Why would you want to assign to it anyway? Here is an example function signature that uses std::string_view:

// Called e.g. with "x86_64-Darwin-16.7.0"
std::string_view extractOSName(std::string_view configStr)
{
    // Parse input, return a new view. Lifetime/ownership managed by caller.
    // No need to re-assign anything, let alone "clearing" them.
}

Upvotes: 5

Related Questions