【问题标题】:Can std::string overload "substr" for rvalue *this and steal resources?std::string 可以为右值 *this 重载“substr”并窃取资源吗?
【发布时间】:2011-06-28 16:34:37
【问题描述】:

我突然想到,当std::stringsubstr 操作可以从*this 窃取分配的内存时,它可能对右值更有效。

N3225的标准库包含std::string的以下成员函数声明

basic_string substr(size_type pos = 0, size_type n = npos) const;

可以为右值实现优化的substr 的实现是否可以重载它并提供两个版本,其中一个可以为右值字符串重用缓冲区?

basic_string substr(size_type pos = 0) &&;
basic_string substr(size_type pos, size_type n) const;

我想右值版本可以实现如下,重用*this的内存,将*this设置为move-from状态。

basic_string substr(size_type pos = 0) && {
  basic_string __r;
  __r.__internal_share_buf(pos, __start + pos, __size - pos);
  __start = 0; // or whatever the 'empty' state is
  return __r;
}

这对常见的字符串实现是否有效,还是需要太多的内务处理?

【问题讨论】:

  • 不会泄露内存吗?毕竟,我们只是窃取了一部分内存,而不是整个区块……
  • 请注意,Java 等其他语言中的字符串会这样做 - 一些问题包括,如​​果您分配一个大字符串,从中提取一个小的子字符串,然后释放大的字符串,您仍然得到只要您使用子字符串,大字符串的数据就会挂在内存中。
  • 这基本上是子字符串的写时复制,不是吗?
  • 我认为这可能需要太多的内务处理才值得。除了其他问题,它似乎不太适合短字符串优化;在获取已经“短”字符串的子字符串时,您可能必须禁用它。
  • 我对委员会正在讨论的这件事有微弱的记忆,但我找不到任何文件。这可能只是一次非正式的讨论。我相信供应商可以自行决定添加此优化。

标签: c++ c++11 substr rvalue-reference


【解决方案1】:

是的,也许应该向标准委员会提出建议,或者在图书馆中实施。我真的不知道优化会有多大价值。这将是一项有趣的研究。

当 gcc 增加对 r-value this 的支持时,应该有人尝试一下并报告它的用处。

【讨论】:

    【解决方案2】:

    首先,实现不能添加窃取源代码的重载,因为这是可以检测到的:

    std::string s="some random string";
    std::string s2=std::move(s).substr(5,5);
    assert(s=="some random string"); 
    assert(s2=="rando");
    

    如果实现从s 窃取数据,第一个断言将失败,并且 C++0x 措辞本质上禁止写入时复制。

    其次,这不一定是优化:您必须在 std::string 中添加额外的内务处理以处理它是较大字符串的子字符串的情况,这意味着在那里时要保留大块不再是引用大字符串的任何字符串,只是它的一些子字符串。

    【讨论】:

    • 这不是真的,因为::std::move 基本上是在说“请从源头窃取数据,这很好!”。
    • std::move 将其强制转换为右值,但标准仍指定该右值允许的内容。由于substr 的唯一指定重载是const,因此它无法修改该值。如果实现提供了一个重载,这是可以检测到的。
    • 请注意,这与s2=std::move(s) 完全不同,因为operator= 的重载需要一个右值引用,所以你不能依赖s 之后的状态---你可以检测实现之间的差异,但您不知道会发生什么。
    • @Anthony,n3225 说“如果函数参数绑定到右值引用参数,则实现可能会假定此参数是对此参数的唯一引用。”。这是否不适用于重载解析添加的成员函数的隐式对象参数?当我们说std::move(s).substr(...),然后仍然使用s 时,在我看来我们会违反这个假设。
    • 是的,但是std::move(s).substr(..) 不会将任何内容绑定到右值引用参数std::move 接受一个左值引用并创建一个右值引用。 substrconst 成员函数,因此隐式对象参数是 const 左值引用。正如我已经说过的,这与s2=std::move(s) 完全不同,其中s 绑定到operator= 的右值引用参数。
    【解决方案3】:

    有一些字符串类实现了写时复制。但我不建议在您的项目中添加另一种字符串类型,除非真的有正当理由。

    查看Memory-efficient C++ strings (interning, ropes, copy-on-write, etc)中的讨论

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-04
      • 1970-01-01
      • 2020-06-06
      • 2018-12-02
      • 1970-01-01
      • 2014-09-03
      • 1970-01-01
      • 2017-10-21
      相关资源
      最近更新 更多