将字符串从stringstream移动到字符串向量中

Moving a string into a vector of strings from a stringstream

本文关键字:字符串 向量 stringstream 移动      更新时间:2023-10-16

我有一个std::stringstream ss和一个std::vector<string> list

我想把push_back(或emplace_back) ss放到list上。

我如何做到这一点,以最好地避免使ss的备份字符串的额外副本?

我的意图是立即运行ss.clear(),因为我将重新填充它(在以后的时间追加到list…)

可能的选项:

  1. list.emplace_back(std::move(ss.str())
  2. list.emplace_back(ss.str())
  3. list.push_back(std::move(ss.str())
  4. list.push_back(ss.str())

每种情况下会发生什么?在每种情况下,ss将保留什么?(无论如何我都要把它扔掉)

这个话题让我不确定该做什么。问题是,我并没有把字符串流移动到另一个字符串流中,我现在特别担心的是能否把作为stringstream构建的字符串(可能是巨大的)移动到字符串容器的后面。显然,如果str()以这样一种方式实现,导致一些复制或转换,这是不可避免的,但希望我想生成的代码,将能够在常数时间内把它塞进我的向量的后面。

我看了str()的实现:

template <class _CharT, class _Traits, class _Allocator>
basic_string<_CharT, _Traits, _Allocator>
basic_stringbuf<_CharT, _Traits, _Allocator>::str() const
{
    if (__mode_ & ios_base::out)
    {
        if (__hm_ < this->pptr())
            __hm_ = this->pptr();
        return string_type(this->pbase(), __hm_, __str_.get_allocator());
    }
    else if (__mode_ & ios_base::in)
        return string_type(this->eback(), this->egptr(), __str_.get_allocator());
    return string_type(__str_.get_allocator());
} 

对于这种情况,思路如下:
1) ss返回一个字符串,它是stringstream文本的内部表示的副本,字符串有一个内部缓冲区,一个指针(这就是为什么移动对字符串有意义,尽管也有SSO)
2)由于返回的字符串是一个临时值,当传递回emplace时,它将在vector中移动construct 就地一个字符串对象,因此不会有内部缓冲区的副本(只有指针和一些整数在临时字符串和新字符串之间交换,这非常便宜)。这里几乎同样适用于push_back。

对于

  1. list.emplace_back (std::移动(ss.str ())
  2. list.emplace_back (ss.str ())
  3. list.push_back (std::移动(ss.str ())
  4. list.push_back (ss.str ())

所有的case都必须做几乎相同的事情(意味着它们不会复制临时字符串缓冲区)。像往常一样,分析并看看哪个更好,但我怀疑在这种情况下是否有明显的差异。

By copy函数返回值默认已被std::移动。即

std::string herp() { return string("lalalalala"); }
std::string derp (herp ());

将使用derp的std::string move构造函数。

如下面的代码所示,将使用move赋值操作符。在内部,字符串自己的operator=(&&)将与字符串ss.str()交换将返回

#include <iostream>
#include <sstream>
#include <string>
using namespace std;
class Herp {
    string internal;
    public:
        const Herp& operator=(const string& s)
        {
            cout<<"Standard initialize operatorn";
            internal = s;
            return *this;
        }
        const Herp& operator=(string&& s)
        {
            cout<<"Move initialize operatorn";
            internal = s;
            return *this;
        }
};
int main()
{
    Herp a;
    stringstream ss;
    ss<<string("abcdef");
    a = ss.str();
    return 0;
}

Success time: 0内存:3428信号:0

移动初始化操作符

如前所述,所有四个选项最终都做同样的事情并且是最优的—它们将str()返回的临时字符串(它是一个右值,因此&&的push_back重载应用)移动到向量的后面。当您希望将一组参数传递给value_type的构造函数时,emplace_back是有用的。考虑:

std::vector<std::pair<std::string, std::string>> vec;
vec.push_back(std::make_pair("Hey", "You")); // a temporary pair is created here
vec.emplace_back("Hey", "You");  // arguments are forwarded to the constructor of
                                 // pair which is constructed in place

对于您的情况,您可以做的最好的是使ss为右值,并调用str()。希望stringstream的实现足够聪明,并意识到它可以只给出它的内部缓冲区,而不是复制它:

list.emplace_back(std::move(ss).str());