std::string::reserve()和std::string::clear()难题

5

这个问题以一段代码开始,因为我认为这样更容易看出我的目的:

/*static*/ 
void 
Url::Split
(std::list<std::string> & url
, const std::string& stringUrl
)
{
    std::string collector;
    collector.reserve(stringUrl.length());
    for (auto c : stringUrl)
    {
        if (PathSeparator == c)
        {
            url.push_back(collector);
            collector.clear(); // Sabotages my optimization with reserve() above!
        }
        else
        {
            collector.push_back(c);
        }
    }
    url.push_back(collector);
}

在上面的代码中,collector.reserve(stringUrl.length());行旨在减少循环下面执行的堆操作量。毕竟,每个子字符串都不能比整个 url 更长,因此像我这样保留足够的容量看起来是一个好主意。
但是,一旦一个子字符串完成并添加到 url 部分列表中,我需要以某种方式将字符串重置为长度为 0。简短的“查看定义”检查让我认为,至少在我的平台上,保留的缓冲区将被释放,这样,我的 reserve() 调用的目的就会受到影响。
在清除的情况下,它内部调用了一些 _Eos(0)
我也可以通过 collector.resize(0) 来实现相同的效果,但是查看定义显示它也内部调用 _Eos(newsize),因此行为与调用 clear() 的情况相同。
现在的问题是,是否有一种便携式的方法来建立预期的优化,并且哪个 std::string 函数可以帮助我做到这一点。
当然,我可以写成 collector[0] = '\0';,但对我来说看起来很奇怪。
附注:虽然我找到了类似的问题,但我不认为这是任何一个问题的重复。
提前致谢。

好问题:通常认为clear不会替换缓冲区,但我在C++11中没有看到任何这样的保证。可能它存在,但不明显。 - Cheers and hth. - Alf
"使用reserve()会破坏我的优化吗?" - Lightness Races in Orbit
@LightnessRacesinOrbit 现在不行,我的当前平台不支持。但我记得有些平台的实现看起来都很不同。不确定是dinkum实现之一还是SGI...嗯...那是几年前的事了。 - BitTickler
@BitTickler:C++现在已经标准化了。 - Lightness Races in Orbit
1个回答

4
在C++11标准中,clear是基于erase定义的,erase被定义为值替换。没有明显的保证缓冲区不会被释放。它可能存在于其他内容中,但我未能找到任何相关信息。
在没有正式保证clear不会被释放的情况下,至少在C++11中似乎没有这样的保证,您有以下选择:
  • 忽略这个问题。
    毕竟,动态缓冲区分配所需的微秒可能完全无关紧要,并且即使没有正式保证,clear释放的机会也非常低。
  • 要求不会释放clear的C++实现。
    (您可以添加一个assert,检查.capacity()。)
  • 自己实现缓冲区。

即使在分配(如果执行)是时间关键的情况下,忽略此问题似乎也是安全的,因为使用常见实现方式,clear不会减少容量
例如,在g++和Visual C++中:
#include <iostream>
#include <string>
using namespace std;

auto main() -> int
{
    string s = "Blah blah blah";
    cout << s.capacity();
    s.clear();
    cout << ' ' << s.capacity() << endl;
}
在进行自己的缓冲管理时,如果您真的想要走得这么远,可以采取以下步骤:
C:\my\so\0284>g++ keep_capacity.cpp -std=c++11 C:\my\so\0284>a 14 14
C:\my\so\0284>cl keep_capacity.cpp /Feb keep_capacity.cpp C:\my\so\0284>b 15 15
C:\my\so\0284>_
#include <iostream>
#include <string>
#include <vector>

namespace my {
    using std::string;
    using std::vector;

    class Collector
    {
    private:
        vector<char>    buffer_;
        int             size_;

    public:
        auto str() const
            -> string
        { return string( buffer_.begin(), buffer_.begin() + size_ ); }

        auto size() const -> int { return size_; }

        void append( const char c )
        {
            if( size_ < int( buffer_.size() ) )
            {
                buffer_[size_++] = c;
            }
            else
            {
                buffer_.push_back( c );
                buffer_.resize( buffer_.capacity() );
                ++size_;
            }
        }

        void clear() { size_ = 0; }

        explicit Collector( const int initial_capacity = 0 )
            : buffer_( initial_capacity )
            , size_( 0 )
        { buffer_.resize( buffer_.capacity() ); }
    };

    auto split( const string& url, const char pathSeparator = '/' )
        -> vector<string>
    {
        vector<string>  result;
        Collector       collector( url.length() );

        for( const auto c : url )
        {
            if( pathSeparator == c )
            {
                result.push_back( collector.str() );
                collector.clear();
            }
            else
            {
                collector.append( c );
            }
        }
        if( collector.size() > 0 ) { result.push_back( collector.str() ); }
        return result;
    }
}  // namespace my

auto main() -> int
{
    using namespace std;
    auto const url = "http://en.wikipedia.org/wiki/Uniform_resource_locator";

    for( string const& part : my::split( url ) )
    {
        cout << '[' << part << ']' << endl;
    }
}

向量在标准中有明确的措辞(“不得重新分配…”,在https://dev59.com/SWMl5IYBdhLWcg3wXF9f中讨论过),但basic_string没有相应的措辞…似乎只是容量方面未明确规定。 - Cubbi
@Cubbi:那么上面的“Collector”类就不必要了,可以直接使用“std::vector”。这就是我记得的,但我并不想坐下来查找标准,所以我只是写了代码。 :) - Cheers and hth. - Alf
@Cubbi: :( 我查了你提到的答案,但是并没有找到。特别是将“不会进行重新分配”解释为防止重新/分配的保证与交换和移动赋值不兼容。好吧,积极的一面是:上面的Collector类毕竟是必要的,只是为了对事情痛苦地严谨。 - Cheers and hth. - Alf

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