How to move std::ostringstream's underlying string object?
Asked Answered
L

2

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();
}

How to move std::ostringstream's underlying string object?

Leguminous answered 8/12, 2016 at 8:50 Comment(8)
I guess you could do it by s = move( oss.rdbuf()->str() ) [ Oh, I checked, you can't], but you risk breaking the assumptions of the stream code by pulling the rug from under its feet.*. Why do you want this?Gen
I think you can invent something interesting using node handle from C++17: en.cppreference.com/w/cpp/container/node_handleKoroseal
This isn't possible, there's no obligation that a std::basic_stringbuf uses a std::basic_string as its underlying buffer (even though this is how it's done in practice).Ninetieth
@user657267: It <del>does</del> APPEARS TO have a string as buffer, that's what the str member functions are all about. But it doesn't provide mutable access to it. Hm.Gen
@Cheersandhth.-Alf If that were the case then the class description would have an "exposition only" private std::basic_string member, the standard only refers to an "underlying character sequence".Ninetieth
Move oss, and then in external code get string?Welford
There is no requirement on stringstream to store the data in contiguious buffer AFAIK. So, to get the string, you will have to construct a string object atleast once.Redcap
I know of two implementations that use a basic_string and one that uses a plain char array.Mymya
J
3

The standard says that std::ostringstream::str() returns a copy.

One way to avoid this copy is to implement another std::streambuf derived-class that exposes the string buffer directly. Boost.IOStreams makes this pretty trivial:

#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';
}
Jersey answered 8/12, 2016 at 11:33 Comment(2)
This copies the string also, because buffer->string is neither a prvalue, nor a temporary, nor does it meet the requirements for copy elision of being the name of a non-volatile object of automatic storage duration (and of the same type as returned). In this case, you have to force a move using e.g. std::move. Since buffer->string cannot possibly be eligible for copy elision in any case, this doesn't prevent other optimizations.Namely
@ArneVogel You are right that there is another copy operation here. Updated.Jersey
C
0

Since C++20 you can.

std::move(oss).str()
Clairvoyance answered 5/1, 2023 at 12:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.