如何有效地为`std :: string`的子字符串获取`string_view`

时间:2017-09-04 07:33:21

标签: c++ view c++17 stdstring

使用http://en.cppreference.com/w/cpp/string/basic_string_view作为参考,我认为没有办法更优雅地做到这一点:

std::string s = "hello world!";
std::string_view v = s;
v = v.substr(6, 5); // "world"

更糟糕的是,天真的做法是一个陷阱,让v悬挂在一个临时的引用上:

std::string s = "hello world!";
std::string_view v(s.substr(6, 5)); // OOPS!

似乎记得这样的东西可能会增加标准库以返回子字符串作为视图:

auto v(s.substr_view(6, 5));

我可以想到以下解决方法:

std::string_view(s).substr(6, 5);
std::string_view(s.data()+6, 5);
// or even "worse":
std::string_view(s).remove_prefix(6).remove_suffix(1);
坦率地说,我认为这些都不是很好。现在我能想到的最好的事情就是使用别名来简化事情。

using sv = std::string_view;
sv(s).substr(6, 5);

3 个答案:

答案 0 :(得分:36)

有自由功能路线,但除非你也为std::string提供过载,否则它就是一个蛇坑。

#include <string>
#include <string_view>

std::string_view sub_string(
  std::string_view s, 
  std::size_t p, 
  std::size_t n = std::string_view::npos)
{
  return s.substr(p, n);
}

int main()
{
  using namespace std::literals;

  auto source = "foobar"s;

  // this is fine and elegant...
  auto bar = sub_string(source, 3);

  // but uh-oh...
  bar = sub_string("foobar"s, 3);
}

恕我直言,string_view的整个设计是一个恐怖表演,它将带我们回到段落错误和愤怒的客户世界。

更新

即使为std::string添加重载也是一个恐怖节目。看看你是否能发现微妙的段错定时炸弹...

#include <string>
#include <string_view>

std::string_view sub_string(std::string_view s, 
  std::size_t p, 
  std::size_t n = std::string_view::npos)
{
  return s.substr(p, n);
}

std::string sub_string(std::string&& s, 
  std::size_t p, 
  std::size_t n = std::string::npos)
{
  return s.substr(p, n);
}

std::string sub_string(std::string const& s, 
  std::size_t p, 
  std::size_t n = std::string::npos)
{
  return s.substr(p, n);
}

int main()
{
  using namespace std::literals;

  auto source = "foobar"s;
  auto bar = sub_string(std::string_view(source), 3);

  // but uh-oh...
  bar = sub_string("foobar"s, 3);
}

编译器在这里没有发现任何警告。我确信代码审查也不会。

之前我已经说过了,我会再说一遍,如果c ++委员会的任何人都在观看,允许从std::stringstd::string_view的隐式转换是一个可怕的错误,只会使c ++变得声名鹊起

更新

在cpporg留言板上提出了这个(对我而言)相当惊人的string_view属性,我的担忧已经得到了冷漠。

这个小组的建议的共识是std::string_view绝不能从函数返回,这意味着我上面的第一个提供是不好的形式。

当然偶然发生这种情况时(例如通过模板扩展),没有编译器帮助来捕获时间。

因此,应该非常小心地使用std::string_view,因为从内存管理的角度来看,它等同于指向另一个对象状态的可复制指针,该对象可能不再存在。但是,它在所有其他方面看起来和行为都像值类型。

因此这样的代码:

auto s = get_something().get_suffix();

get_suffix()返回std::string(按值或参考)

时是否安全

但如果get_suffix()被重构为返回std::string_view,则为UB。

在我的简略视图中,这意味着,如果他们调用的库被重构为返回auto代替std::string_view,那么使用std::string const&存储返回字符串的任何用户代码都将中断。

所以从现在开始,至少对我来说,“几乎总是自动”必须变成“几乎总是自动的,除非它是字符串”。

答案 1 :(得分:3)

您可以使用 std :: string 中的转换运算符到 std :: string_view

std::string s = "hello world!";
std::string_view v = std::string_view(s).substr(6, 5);

答案 2 :(得分:0)

这是有效地创建子字符串string_view的方法。

#include <string>
inline
std::string_view substr_view(const std::string &s,size_t from,size_t len) {
  if( from>=s.size() ) return {};
  return std::string_view(s.data()+from,std::min(s.size()-from,len));
}

#include <iostream>
int main(void) {
  std::cout << substr_view("abcd",3,11) << "\n";

  std::string s {"0123456789"};
  std::cout << substr_view(s,3,2) << "\n";

  return 0;
}