back_inserter
а также insert_iterator
очень удобны, но они также очень неэффективны!
Когда вы добавляете char
s, например, есть много накладных расходов для каждого элемента, когда вы copy
Однако, когда во многих ситуациях это действительно не нужно.
Есть ли способ сделать их более эффективными?
Да, вы можете определить новую версию std::copy
который может похитить оптимизированные звонки. 🙂
Ниже приведен пример (или «хак», если вы предпочитаете, чтобы стекло было полупустым) для Visual C ++ и GCC.
На моем персональном компьютере (я использую VC ++ 2010), код ниже делает звонки десять раз Быстрее!
Здесь также есть тест для GCC, показывающий разницу в 5 раз: старая версия против новая версия
Обратите внимание, что этот код предполагает, что контейнер обеспечивает vector
интерфейс.
Как написано в настоящее время, это работает только для C ++ 11, потому что он использует type_traits
возможности метапрограммирования заголовка только оптимизировать те ситуации, в которых операция копирования останется безопасной для исключения.
Если вам не нужна безопасность исключений (хотя вы должны дважды подумать, прежде чем делать это), или если у вас есть другой способ проверки таких типов данных, вы можете изменить
typename enable_if<..., typename insert_iterator<C> >::type
чтобы:
insert_iterator<C>
и остальная часть кода должна работать для 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);
}
}
Других решений пока нет …