Search code examples
c++stringc++17std

Is replacing all const std::string & with std::string_view a good choice?


I am very sensitive about memory allocation and copy. So, if a function needs a std::string object as input, I always use const std::string &.

Recently, I found const std::string & will contruct a std::string object if I pass in a char[]:

#include <iostream>
#include <string>
         
using namespace std;
         
void test_string(const std::string & s) {  // use std::string_view here is better
  // it can avoid local variable allocation
  printf("%p\n", s.data());
}        
         
int main() {
  char aa[] = "asd";
  test_string(aa);
  printf("%p\n", aa);
}

I changed the const std::string & to std::string_view, and it solved the unnecessary construction or copy. So, I think std::string_view is worthy to be used everywhere.

I replaced all const std::string & with std::string_view, if there is some place I need to use std::string, I use .data() to get the string.

Problems happen in the following sitution:

inline std::vector<std::string_view> Split(std::string_view str, std::string_view delim, const bool trim_empty = false) {
    if (str.empty()) return {}; 
    size_t pos, last_pos = 0, len;
    std::vector<std::string_view> tokens;
    while (true) {
      pos = str.find(delim, last_pos);
      if (pos == std::string::npos) pos = str.size();
      len = pos - last_pos;
      if (!trim_empty || len != 0) tokens.push_back(str.substr(last_pos, len));
      if (pos == str.size()) break; 
      else last_pos = pos + delim.size();
    }   
    return tokens;
  }

I use this to split a string into a vector, as you can see, the std::string_view avoids a copy and big memory allocation.

But the problem happens in using the std::string_view:

std::string str = "asd\nbn\ncd\n\n";
std::unordered_map<std::string, int> m;
m["asd"] = 2;
const auto & v = Split(str, "\n");
const auto & it = m.find(v.front().data());

This failed because v.front().data() returns the whole string, not the first part.

I know this is caused by no "\0" in the string stream.

It seems there is no good way to find the correct std::string_view, except for constructing a std::string using std::string(v.front().data()).

So, is there any way I can make .data() end as expected? Or is replacing const std::string & with std::string_view just not always a good choice?


Solution

  • std::string_view::data() returns a pointer that is not necessarily null-terminated.
    std::string::data() returns a pointer that is always null-terminated (since C++11).
    std::string constructor 9 that accepts char* requires it to point to a null-terminated string.

    Since you construct temporary std::string anyway, do it properly: std::string{v.front()} will construct string of correct length from std::string_view.

    Or, if this is the only use of Split, don't use std::string_view at all, there doesn't seem to be a lot of gain if you use string_view only to construct string out of it.


    Also, outside of the question, but as Botje noticed in comment, your usage of references is wrong. The objects they refer to both die at ; ending respective line and you are left with dangling references.