【问题标题】:Is there a way make a std::string that references an externally provided buffer but not own it?有没有办法制作一个引用外部提供的缓冲区但不拥有它的 std::string ?
【发布时间】:2022-03-01 02:53:46
【问题描述】:

基本上,如果一个人有一个空终止字符串的预加载缓冲区和要引用的长度,并且想要将一个引用传递给一个方法,该方法采用 std::string & 但不复制字符串或拥有它,有可能吗?

这只会有一个有限的生命周期,它的管理方式是它只在缓冲区有效时才有效。

【问题讨论】:

  • 不,不可能。你的方法应该一直采用std::string_view
  • 您所描述的是string_view。如果您无法更改您正在调用的成员函数(方法不是 C++ 术语)或其接口,理论上您可以使用自己的实现创建一个派生自 string 的类。但是,basic_string 中的相关函数未声明为 virtual,并且在作为基类类型访问时无法获得新的行为。

标签: c++ string reference zero-copy


【解决方案1】:

有没有办法创建一个引用外部提供的缓冲区但不拥有它的 std::string?

没有。

您有以下选择:

  1. 首先使用std::string 作为“外部”缓冲区。
  2. 将外部缓冲区复制到字符串中。
  3. 不要使用(参考)std::string 作为参数。
    • std::string_view 通常是一个不错的选择。但是,创建非空终止字符串视图非常容易,并且您的前提明确声明空终止。如果这很重要,那么您可能需要避免使用字符串视图。
    • 如果字符串视图不合适,那么您可以使用const char* 指向以空结尾的字符串。

【讨论】:

  • 你的第一个低于 3 的子弹是错误的。源是一个以空结尾的字符串,而目标想要std::string-like,所以没关系。
  • @Caleth 这完全取决于函数对它的作用。 std::string 也是空终止的。
  • 似乎答案是否定的——也就是说,您不能提供外部缓冲区来创建 std::string 以在不复制字符串内容的情况下传递给方法。我提到空终止的原因是 AFAIK 与 std::string API 的合同是它是空终止的。另一个原因是,如果一个人有一个具有另一种字符串类型的环境,并且希望将其传递给采用 std::string 而不复制字符串的遗留方法。
  • @peterk 在 OP 问题中,所有字符串都以空值结尾。然而,它们不能在不复制的情况下作为 std::string 传递。所以这个标准不是一个充分的理由(它只是必要的)。
【解决方案2】:

基本上,非拥有字符串的答案是否定的。

但是,如果非拥有条件不是那么重要,您可以做的是使用自己的分配器来引用特定的缓冲区。

您还可以做的是使用std::pmr::string,它允许您提供自定义memory_resource

思路如下:

#include <string>
#include <memory_resource>
#include <array>
#include <utility>
#include <iostream>

template<std::size_t size>
class StackBufferResource {
    public:
        auto as_resource() {return &m_resource;}
        auto address() {return m_buffer.data();}
    private:
        std::array<std::byte, size> m_buffer{};
        std::pmr::monotonic_buffer_resource m_resource{m_buffer.data(), size};
};

int main() {
    StackBufferResource<512> buffer;

    std::pmr::string myString("My name is Antoine and I am not sure for this answer", buffer.as_resource());
    std::cout << myString << "\n";
    std::cout << (const char*)buffer.address() << std::endl;
}

std::pmr::monotonic_buffer_resource 是一个不断增长的memory_resource 对象。这意味着释放是一种“无操作”。

这样的好处是你可以把同样的东西给std::pmr::vector

不过,你可以注意以下几点:

std::pmr::string 使用char。由于它是一个微不足道的对象,我认为(虽然我不确定)在字符串被破坏后访问缓冲存储器是安全的。 如果它是一种不可轻易破坏的类型,我认为可能会看到垃圾值。

【讨论】:

  • 是的,这可能是拥有一个不会删除缓冲区并可以分配它的分配器的唯一方法。我已经用其他一些 std::objects 完成了这个问题是为了防止复制到缓冲区中并找出一种方法来预加载它,因为其中已经存在数据。许多遗留代码需要非常大的 const std::string & 作为输入,并且复制变得非常重要。
  • 确保您永远不想访问已释放的内存 - 如果我想要这个,它将在外部进行管理 - 目的是可以将大字符串传递到不拥有该字符串的库例程中,并且当他们不再需要它时会通知您。 const char *hugeText = getBufferFromSystem(locator);像 parseHugeText(maketmpString(hugeText)): systemRelease(hugeText);可以使用分配器将其设置为巨大的文本,然后不释放它。但前提是 std::string 在“分配”后不改变缓冲区的内容。
猜你喜欢
  • 2011-07-19
  • 2015-06-03
  • 2019-10-18
  • 2023-03-08
  • 2010-10-27
  • 1970-01-01
  • 2017-10-04
  • 1970-01-01
  • 2016-09-10
相关资源
最近更新 更多