back_inserter
和insert_iterator
非常方便,但它们也非常低效!
例如,当你添加char
时,每个元素在进行copy
操作时都有很多开销,而在许多情况下,实际上并不需要这样。
有没有办法使它们更有效率?
是的,您可以定义一个新版本的std::copy
,它可以劫持可优化的调用。 :)
以下是Visual C++和GCC的示例(或者如果您更喜欢看到半杯空的杯子,则为“hack”)。
在我的个人电脑上(我使用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);
}
}
std
命名空间内添加那些特殊化。你可以使用相同的代码,但必须放在该命名空间之外。根据 output
的类型,强制转换 static_cast<It&>(output)
也可能是未定义的行为。 - David Rodríguez - dribeasstd::copy
的重载,而不是函数模板的特化。 - David Rodríguez - dribeas
y.insert(y.end(), x.begin(), x.end())
对我来说与快速方法表现相同。我怀疑只有当你有一个调用copy
的通用函数并使用insert_iterator
调用copy
的性能时,你才会关心这个问题,因为你通常不会故意这样做。既然你的代码真正体现了两者之间的差异,那就没问题了。 - Steve Jessopstd::copy
。这大概是我能想象到的最“正常”的代码了。 - user541686