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

14

基本上,如果有一个预加载了空终止字符串的缓冲区和要引用的长度,并且想将其引用传递到一个接受std::string&但不复制字符串或拥有它的方法中,是否可以这样做?

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


21
дёЌиЎЊпјЊж— жі•жЋҐеЏ—гЂ‚дЅ зљ„ж–№жі•еє”иЇҐдЅїз”Ёstd::string_viewдЅњдёєеЏ‚ж•°гЂ‚ - HolyBlackCat
2
您所描述的是 string_view。如果您无法更改正在调用的成员函数(方法不是 C++ 术语)或其接口,理论上可以创建一个从 string 派生的类,并使用自己的实现。然而,在作为基类类型访问时,basic_string 中的相关函数未声明为 virtual,因此无法获得新行为。 - Sebastian
2个回答

12
有没有一种方法可以创建一个 std::string,它引用外部提供的缓冲区但不拥有它?
没有。
您有以下选项:
1. 从一开始就将 std::string 用作“外部”缓冲区。 2. 将外部缓冲区复制到字符串中。 3. 不要使用(引用)std::string 作为参数。 - std::string_view 是一个通常不错的选择。然而,非空终止的字符串视图很容易创建,而您的前提明确说明了空终止。如果这很重要,则可能需要避免使用字符串视图。 - 如果字符串视图不合适,则可以使用 const char* 指向以空字符结尾的字符串。

1
你在第3点下的第一个要点是颠倒了。源是一个以空字符结尾的字符串,而目标需要类似于std::string的东西,所以没问题。 - Caleth
3
这完全取决于函数对它的处理方式。std::string 也是以 null 终止的。 - eerorika
似乎答案是“不能”——也就是说,您无法提供一个外部缓冲区来创建std::string,并将其传递给方法而不复制字符串内容。我提到空终止符的原因是根据我所知,std::string API的约定是它以空终止符结尾。另一个原因是,如果有另一种字符串类型的环境,并且想要将其传递给使用std::string的遗留方法而不复制该字符串,则可以这样做。 - peterk
@peterk 在 OP 的问题中,所有字符串都是以空字符结尾的。然而,它们不能作为 std::string 直接传递而不进行复制。因此,这个标准并不足以成为充分的理由(只是必要条件)。 - Sebastian

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。由于它是一个平凡的对象,我认为(尽管不确定)在字符串被销毁后访问缓冲区内存是安全的。 如果它是一个非平凡析构类型,我认为可能会看到垃圾值。


1
是的,可能唯一的方法是拥有一个分配器,它不会删除缓冲区并且可以分配它。我已经在一些其他std::对象中做到了这一点。问题在于防止将数据复制到缓冲区,并找出一种方式使其预加载为已存在的数据。许多旧代码需要大量的const std::string &作为输入,而复制变得非常重要。 - peterk
1
确保您永远不要访问已释放的内存 - 如果我想要这样做,它将由外部进行管理 - 目的是使您可以将大字符串传递到不拥有该字符串并在不再需要时通知您的库例程中。const char *hugeText = getBufferFromSystem(locator);例如 parseHugeText(maketmpString(hugeText)):systemRelease(hugeText);可以使用分配器来设置为巨大文本,然后不释放它。但前提是 std::string 在“分配”后不会更改缓冲区的内容。 - peterk
std::pmr::string虽然不是std::string。但是,如果问题的提问者_真的_需要std::string(例如,用于某个第三方库,无法更改类型),那么std::pmr::string将无法帮助。如果他们可以更改类型,那么std::string_view是正确的选择。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接