如何移动std::ostringstream的底层字符串对象?

8
#include <sstream>
#include <string>

using namespace std;

template<typename T>
string ToString(const T& obj)
{
    ostringstream oss;
    oss << obj;

    //
    // oss will never be used again, so I should
    // MOVE its underlying string.
    //
    // However, below will COPY, rather than MOVE, 
    // oss' underlying string object!
    //
    return oss.str();
}

如何移动 std::ostringstream 的基础字符串对象?


2
我猜你可以通过s = move( oss.rdbuf()->str() )来实现[哦,我检查了,不行],但是这样做会冒着从流代码中拉出地毯的风险,从而破坏其假设。你为什么想要这个? - Cheers and hth. - Alf
1
我认为你可以使用C++17中的node handle创造出一些有趣的东西:http://en.cppreference.com/w/cpp/container/node_handle - alexeykuzmin0
4
这是不可能的,因为没有义务让 std::basic_stringbuf 使用 std::basic_string 作为其底层缓冲区(即使实际上它是这样做的)。 - user657267
2
@user657267:它似乎有一个字符串作为缓冲区,这就是str成员函数的全部内容。但它不提供对其进行可变访问的方式。嗯。 - Cheers and hth. - Alf
5
我知道有两个使用basic_string的实现以及一个使用普通的char数组的实现。 - T.C.
显示剩余3条评论
2个回答

3
标准规定std::ostringstream::str()返回一个副本

避免这种复制的一种方法是实现另一个std::streambuf派生类,直接公开字符串缓冲区。Boost.IOStreams使这个过程非常简单:

#include <boost/iostreams/stream_buffer.hpp>
#include <iostream>
#include <string>

namespace io = boost::iostreams;

struct StringSink
{
    std::string string;

    using char_type = char;
    using category = io::sink_tag;

    std::streamsize write(char const* s, std::streamsize n) {
        string.append(s, n);
        return n;
    }
};

template<typename T>
std::string ToString(T const& obj) {
    io::stream_buffer<StringSink> buffer{{}};

    std::ostream stream(&buffer);
    stream << obj;
    stream.flush();

    return std::move(buffer->string); // <--- Access the string buffer directly here and move it.
}

int main() {
    std::cout << ToString(3.14) << '\n';
}

2
这也会复制字符串,因为buffer->string既不是prvalue,也不是temporary,也不满足副本省略的要求,即作为自动存储期非易失性对象的名称(并且与返回的类型相同)。在这种情况下,必须使用例如“std::move”来强制移动。由于buffer->string在任何情况下都不可能有资格进行副本省略,因此这并不会防止其他优化。 - Arne Vogel
@ArneVogel 你说得对,这里还有另一个复制操作。已更新。 - Maxim Egorushkin

0

自C++20以来,您可以这样做。

std::move(oss).str()

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