用 std::string_view 替换所有 const std::string & 是一个不错的选择吗?

问题描述 投票:0回答:2

我对内存分配和复制非常敏感。因此,如果函数需要

std::string
对象作为输入,我总是使用
const std::string &

最近,我发现如果我传入一个

const std::string &
std::string
将会构造一个
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);
}

我将

const std::string &
改为
std::string_view
,解决了不必要的构建或复制。所以,我认为
std::string_view
值得在任何地方使用。

我把所有

const std::string &
都换成了
std::string_view
,如果有地方需要用
std::string
,我就用
.data()
来获取字符串。

以下情况会出现问题:

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;
  }

我用它来将

string
拆分为
vector
,如您所见,
std::string_view
避免了复制和大量内存分配。

但是问题发生在使用

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

此操作失败,因为

v.front().data()
返回整个字符串,而不是第一部分。

我知道这是由于字符串流中没有

"\0"
造成的。

似乎没有什么好方法可以找到正确的

std::string_view
,除了使用
std::string
构造一个
std::string(v.front().data())

那么,有什么办法可以让

.data()
如期结束呢?或者用
const std::string &
替换
std::string_view
并不总是一个好的选择?

c++ string stl c++17
2个回答
4
投票

std::string_view::data()
返回一个不一定以 null 终止的指针。
std::string::data()
返回一个 always 以 null 终止的指针 (C++11 起)。
接受
std::string
char*
构造函数 9
要求它指向以 null 结尾的字符串。

既然你无论如何都要构造临时

std::string
,请正确执行:
std::string{v.front()}
将从
std::string_view
构造正确长度的字符串。

或者,如果这是

Split
的唯一用途,则根本不要使用
std::string_view
,如果仅使用
string_view
来构造
string
似乎不会有太多收获.


此外,在问题之外,但正如 Botje 在 comment 中注意到的那样,您对引用的使用是错误的。它们引用的对象都在相应行的

;
处消失,您只剩下悬空引用。


0
投票

这里的主要问题是,您构建

std::string
的唯一目的是与
map.find
一起使用,在这种情况下,您可能想要调用接受
std::string
而不是传递
string_view
的构造函数
char*
所以不需要调用
strlen
。 (明确地做
std::string{string_view}
)但这不是问题的真正解决方案。

通常

std::unordered_map
具有
find
并且比较器和哈希器不允许其他类型,因此如果
find
string_view
,则
Key
对于
std::string
会失败,您将必须定义自己的哈希器和比较器,并且您将需要 C++20 来进行重载,以接受与 find

的键相当的任何类型
#include <unordered_map>
#include <string>
#include <string_view>
#include <iostream>

struct Comparator
{
    using is_transparent = std::true_type;

    bool operator()(const auto& lhs, const auto& rhs) const { 
        auto result = lhs == rhs;
        return result;
    }
};
struct Hash
{
    using is_transparent = std::true_type;

    template <typename T>
    size_t operator()(const T& obj) const { 
        auto result = std::hash<std::decay_t<T>>{}(obj);
        return result;
    }
};

int main()
{
    std::unordered_map<std::string, int, Hash, Comparator> m{ {"hel",1} };
    std::string s = "hello";
    std::string_view sv{ s };
    sv = sv.substr(0, 3);
    auto it = m.find(sv);
    if (it != m.end())
    {
        std::cout << "found!";
    }
}

请注意,您需要 C++20 编译器来运行此代码,否则您将必须使用 Boost 的容器版本,该容器已经具有针对较低 C++ 版本的此重载,或具有此重载的任何其他实现。

© www.soinside.com 2019 - 2024. All rights reserved.