使用 back_inserter() 或 inserter() 提高 std::copy() 的效率

Meh*_*dad 6 c++ inserter

back_inserter非常insert_iterator方便,但效率也很低!

例如,当您追加chars 时,每个元素都会产生大量开销copy,而在许多情况下,实际上并不需要如此。

有没有办法让他们更有效率?

Meh*_*dad 1

std::copy是的,您可以定义一个可以劫持可优化调用的新版本。:)

下面是 Visual C++ 和 GCC 的示例(或者“hack”,如果您更喜欢看到半空的玻璃杯)。

在我的个人计算机上(我使用 VC++ 2010),下面的代码使调用速度提高了十倍
GCC 的基准测试也在这里,显示了 5 倍的差异:旧版本新版本

但在使用它之前:

请注意,此代码假设容器提供vector类似接口

按照目前的编写,这仅适用于 C++11,因为它使用type_traits标头的元编程功能来优化复制操作保持异常安全的情况。

如果您不需要异常安全(尽管在实际执行此操作之前应该三思而后行),或者如果您有另一种方法来检查此类数据类型,那么您可以更改

typename enable_if<..., typename insert_iterator<C> >::type
Run Code Online (Sandbox Code Playgroud)

到:

insert_iterator<C>
Run Code Online (Sandbox Code Playgroud)

其余代码也应该适用于 C++03。

namespace std
{
    template<class FwdIt, class C>
    back_insert_iterator<C> copy(
        FwdIt begin, FwdIt end, back_insert_iterator<C> it,
        forward_iterator_tag * =
          static_cast<typename iterator_traits<FwdIt>::iterator_category *>(0))
    {
        struct It : public back_insert_iterator<C>
        {
            using back_insert_iterator<C>::container;
            static C &deref(C &c) { return  c; }
            static C &deref(C *c) { return *c; }
        };
        copy(begin, end, inserter(It::deref(static_cast<It &>(it).container),
                      It::deref(static_cast<It &>(it).container).end()));
        return it;
    }

    template<class FwdIt, class C>
    typename enable_if<  // Only do this if it would be exception-safe!
        is_nothrow_copy_constructible<typename C::value_type>::value &&
        is_nothrow_copy_assignable<typename C::value_type>::value,
        insert_iterator<C>
    >::type copy(
        FwdIt const &begin, FwdIt const &end,
        insert_iterator<C> output,
        forward_iterator_tag * =                  // only forward iterators
          static_cast<typename iterator_traits<FwdIt>::iterator_category *>(0))
    {
        struct It : public insert_iterator<C>
        {
            using insert_iterator<C>::container;  // protected -> public
            using insert_iterator<C>::iter;       // protected -> public
            static C &deref(C &c) { return  c; }
            static C &deref(C *c) { return *c; }
        };
        It &it(static_cast<It &>(output));
        typename C::iterator it_iter_end(It::deref(it.container).end());
        {
            // Convert iterators to offsets
            typename C::size_type const iter_end_off =
                std::distance(It::deref(it.container).begin(), it_iter_end);
            typename iterator_traits<typename C::iterator>::difference_type off
                = std::distance(It::deref(it.container).begin(), it.iter);

            // Resize container
            It::deref(it.container).resize(
                It::deref(it.container).size() +
                static_cast<typename C::size_type>(std::distance(begin, end)));
            
            // Renormalize, in case invalidated
            it.iter = It::deref(it.container).begin();
            std::advance(it.iter, off);
            it_iter_end = It::deref(it.container).begin();
            std::advance(it_iter_end, iter_end_off);
        }
        typename C::iterator result
          = copy_backward(it.iter, it_iter_end, It::deref(it.container).end());
        copy_backward(begin, end, result);
        return inserter(It::deref(it.container), result);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不允许您在“std”命名空间内添加这些专业化。您可以使用相同的代码,但它必须位于该名称空间之外。根据“output”的类型,强制转换“static_cast&lt;It&amp;&gt;(output)”也可能是未定义的行为。 (2认同)