将字符串移出std :: ostringstream

gal*_*tte 27 c++ ostringstream move-semantics c++11

如果我构造一个由空格分隔的浮点值列表组成的字符串,使用std::ostringstream:

std::ostringstream ss;
unsigned int s = floatData.size();
for(unsigned int i=0;i<s;i++)
{
    ss << floatData[i] << " ";
}
Run Code Online (Sandbox Code Playgroud)

然后我得到一个结果std::string:

std::string textValues(ss.str());
Run Code Online (Sandbox Code Playgroud)

但是,这将导致不必要的字符串内容的深层副本,因为ss将不再使用.

有没有办法在不复制整个内容的情况下构造字符串?

Nic*_*asM 17

现在可以使用 C++20 实现这一点,语法如下:

const std::string s = std::move(ss).str();
Run Code Online (Sandbox Code Playgroud)

这是可能的,因为该类std::ostringstream现在具有str()右值引用限定的重载:

basic_string<charT, traits, Allocator> str() &&;  // since C++20
Run Code Online (Sandbox Code Playgroud)

这是在P0408修订版 7 中添加的,并被C++20 采用

这正是 @MarcGlisse 在 2014 年 10 月的一篇有先见之明的评论中建议的方法。


Cub*_*bbi 11

std::ostringstream提供没有公共接口来访问其内存缓冲区,除非它不支持pubsetbuf(但即使这样你的缓冲区是固定大小的,请参阅cppreference示例)

如果要折磨某些字符串流,可以使用受保护的接口访问缓冲区:

#include <iostream>
#include <sstream>
#include <vector>

struct my_stringbuf : std::stringbuf {
    const char* my_str() const { return pbase(); } // pptr might be useful too
};

int main()
{
    std::vector<float> v = {1.1, -3.4, 1/7.0};
    my_stringbuf buf;
    std::ostream ss(&buf);
    for(unsigned int i=0; i < v.size(); ++i)
        ss << v[i] << ' ';
    ss << std::ends;
    std::cout << buf.my_str() << '\n';
}
Run Code Online (Sandbox Code Playgroud)

直接访问自动调整大小的输出流缓冲区的标准C++方法由std::ostrstreamC++ 98中提供,但仍然是标准的C++ 14和计数.

#include <iostream>
#include <strstream>
#include <vector>

int main()
{
    std::vector<float> v = {1.1, -3.4, 1/7.0};
    std::ostrstream ss;
    for(unsigned int i=0; i < v.size(); ++i)
        ss << v[i] << ' ';
    ss << std::ends;
    const char* buffer = ss.str(); // direct access!
    std::cout << buffer << '\n';
    ss.freeze(false); // abomination
}
Run Code Online (Sandbox Code Playgroud)

但是,我认为最干净(也是最快)的解决方案是boost.karma

#include <iostream>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;
int main()
{
    std::vector<float> v = {1.1, -3.4, 1/7.0};
    std::string s;
    karma::generate(back_inserter(s), karma::double_ % ' ', v);
    std::cout << s << '\n'; // here's your string
}
Run Code Online (Sandbox Code Playgroud)


seh*_*ehe 5

+1 为@Cubbi 的 Boost Karma 和“创建您自己的streambuf不制作副本的派生类型,并将其提供给 a 的构造函数”的建议basic_istream<>.

但是,缺少一个更通用的答案,它介于两者之间。它使用 Boost Iostreams:

using string_buf = bio::stream_buffer<bio::back_insert_device<std::string> >;
Run Code Online (Sandbox Code Playgroud)

这是一个演示程序:

Live On Coliru

#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream_buffer.hpp>

namespace bio = boost::iostreams;

using string_buf = bio::stream_buffer<bio::back_insert_device<std::string> >;

// any code that uses ostream
void foo(std::ostream& os) {
    os << "Hello world " 
       << std::hex << std::showbase << 42
       << " " << std::boolalpha << (1==1) << "\n";
}

#include <iostream>

int main() {
    std::string output;
    output.reserve(100); // optionally optimize if you know roughly how large output is gonna, or know what minimal size it will require

    {
        string_buf buf(output);
        std::ostream os(&buf);
        foo(os);
    }

    std::cout << "Output contains: " << output;
}
Run Code Online (Sandbox Code Playgroud)

请注意,您可以轻松地替换std::stringwithstd::wstringstd::vector<char>等。

更好的是,您可以将它与array_sink设备一起使用并拥有一个固定大小的缓冲区。这样您就可以避免使用 Iostreams 代码进行任何缓冲区分配!

Live On Coliru

#include <boost/iostreams/device/array.hpp>

using array_buf = bio::stream_buffer<bio::basic_array<char>>;

// ...

int main() {
    char output[100] = {0};

    {
        array_buf buf(output);
        std::ostream os(&buf);
        foo(os);
    }

    std::cout << "Output contains: " << output;
}
Run Code Online (Sandbox Code Playgroud)

两个程序都打印:

Output contains: Hello world 0x2a true
Run Code Online (Sandbox Code Playgroud)