简体   繁体   English

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

[英]Improving efficiency of std::copy() with back_inserter() or inserter()

back_inserter and insert_iterator are very handy, but they're also very inefficient! back_inserterinsert_iterator非常方便,但它们也非常低效!

When you're appending char s, for example, there is a great deal of overhead for every element when you're copy ing, when in many situations, there really doesn't need to be. 例如,当你附加char ,当你copy时,每个元素都会产生大量的开销,而在很多情况下,实际上并不需要。

Is there a way to make them more efficient? 有没有办法让它们更有效率?

Yes, you can define a new version of std::copy which can hijack optimizable calls. 是的,您可以定义一个新版本的std::copy ,它可以劫持可优化的调用。 :) :)

Below is an example (or "hack", if you prefer to see the glass half-empty) for Visual C++ and GCC. 以下是Visual C ++和GCC的示例(或“hack”,如果您希望看到半空的玻璃)。

On my personal computer (I use VC++ 2010), the code below makes calls ten times faster ! 在我的个人计算机上(我使用VC ++ 2010),下面的代码使调用十倍
A benchmark for GCC's is also here, showing a 5x difference: old version against new version GCC的基准也在这里,显示了5倍的差异: 旧版本新版本的对比

But before you use it: 使用它之前

Note that this code assumes the container provides a vector -like interface . 请注意,此代码假定容器提供了类似vector的接口

As currently written, this only works for C++11, because it uses the type_traits header's metaprogramming capabilities to only optimize those situations in which the copy operation would stay exception-safe. 由于目前写的,这仅适用于C ++ 11,因为它使用了type_traits头的元编程功能, 优化那些在复制操作将保持异常安全的情况。

If you don't need the exception safety (though you should think twice before actually doing this), or if you have another way of checking for such data types, then you can change 如果您不需要异常安全(尽管您在实际执行此操作之前应该三思而行),或者如果您有其他方法来检查此类数据类型,那么您可以更改

typename enable_if<..., typename insert_iterator<C> >::type

to: 至:

insert_iterator<C>

and the rest of the code should work for C++03 as well. 其余的代码也适用于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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM