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

seh*_*ehe 64 c++ view stdstring c++17

使用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"
Run Code Online (Sandbox Code Playgroud)

更糟糕的是,天真的方法是一个陷阱,并留下v暂时的悬挂参考:

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

似乎记得有些东西可能会添加到标准库中以返回子字符串作为视图:

auto v(s.substr_view(6, 5));
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

坦率地说,我认为这些都不是很好.现在我能想到的最好的事情是使用别名来简化事情.

using sv = std::string_view;
sv(s).substr(6, 5);
Run Code Online (Sandbox Code Playgroud)

Ric*_*ges 39

有自由功能的路线,但除非你也提供过载,因为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);
}
Run Code Online (Sandbox Code Playgroud)

恕我直言,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);
}
Run Code Online (Sandbox Code Playgroud)

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

我以前说过,我再说一遍,在对C++委员会万一有人在看,允许隐式转换从std::stringstd::string_view是一个可怕的错误,只会把C++蒙羞.

更新

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

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

当然,当事故发生时(例如通过模板扩展),没有编译器帮助来捕获时间.

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

因此代码如下:

auto s = get_something().get_suffix();
Run Code Online (Sandbox Code Playgroud)

get_suffix()返回时是安全的std::string(通过值或引用)

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

在我的简洁视图中,这意味着任何存储返回字符串的用户代码auto都会破坏,如果他们调用的库被重构而std::string_view不是代替std::string const&.

因此,从现在开始,至少对我而言,"几乎总是自动"必须变成"几乎总是自动的,除非是它的字符串".

  • @sehe我觉得令人惊讶的是委员会没有看到这一点.似乎一旦他们重视现有算法的快速修复性能改进而不是代码安全性.我担心这是一个严重的错误. (8认同)
  • @sehe当然我理解其基本原理.它很有吸引力.如果string_view不可复制且不可移动,则可以安全地实现.然后你无法从函数中返回一个并且一切都会好的. (6认同)
  • @geza你的问题很好地证实了我的担忧.对`bar`的第二个赋值是``string_view`,它是从一个临时`std :: string`构造的`string_view`构造的,现在已经被破坏了. (6认同)
  • @sehe用更糟糕的情况更新 - 好意图出错的结果.期待很快在您附近的代码库中看到这个... (3认同)
  • 谁说他们没有看到它的到来?它一定是称重选项.我的意思是,`string foo(); bool bar(string_view); auto check = bar(foo());`想要允许是安全合理的. (3认同)
  • @sehe:Richard Hodges所展示的正是为什么不允许隐式转换为``const char*``的原因.委员会的推理并不一致,我完全同意RH,很可能会让很多开发人员(特别是新人)痛苦不堪. (3认同)
  • @MartinBonner如果所有不相关的设计咆哮位都去了,我会很高兴的.我在这里的第一条评论显示了一条切线问题的单行[`= delete`声明处理](http://coliru.stacked-crooked.com/a/8e353f2a8f584998). (3认同)
  • @einpoklum这个问题并不难理解,但首先它必须由基本上所有程序员__and__理解_它必须经常在他们的脑海里.那,我不认为是非常安全的.这类似于再次教导指针. (2认同)
  • @WhiZTiM 在核心指南中如何看待这一点,“在无意义的追求美观的代码中,您不应创建假装是值的对象。指针是指针,值是值。” (2认同)
  • @sehe在c ++ 11之前我会同意你的。但是现在它的名字叫“ auto”。**汽车**。看到?通过查看用户代码是否是安全的值对象还是危险的参考对象,根本没有任何线索。 (2认同)

CAF*_*CAF 5

您可以使用从std :: stringstd :: string_view的转换运算符:

std::string s = "hello world!";
std::string_view v = std::string_view(s).substr(6, 5);
Run Code Online (Sandbox Code Playgroud)

  • 这个已经在问题中提到,标记为“不好” (14认同)
  • 恕我直言,这是一个比公认的解决方案要好得多的解决方案。 (4认同)
  • @caps no,[`string_view::substr`](https://en.cppreference.com/w/cpp/string/basic_string_view/substr) 返回一个基于原始视图的视图。如果他们将其称为“子视图”,也许会不会那么混乱? (3认同)

Ale*_*der 5

这就是如何有效地创建子字符串 string_view。

#include <string>
inline std::string_view substr_view(const std::string& source, size_t offset = 0,
                std::string_view::size_type count = 
                std::numeric_limits<std::string_view::size_type>::max()) {
    if (offset < source.size()) 
        return std::string_view(source.data() + offset, 
                        std::min(source.size() - offset, count));
    return {};
}

#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";

  // be cautious about lifetime, as illustrated at https://en.cppreference.com/w/cpp/string/basic_string_view
  std::string_view bad = substr_view("0123456789"s, 3, 2); // "bad" holds a dangling pointer
  std::cout << bad << "\n"; // possible access violation

  return 0;
}
Run Code Online (Sandbox Code Playgroud)