std :: back_inserter比std :: inserter有什么好处?

时间:2014-10-10 20:27:59

标签: c++ vector stl iterator containers

据我所知,std::back_inserter在STL算法中有效,您可以传递std::inserter .end()代替std::copy(l.begin(), l.end(), std::back_inserter(dest_list)); std::copy(l.begin(), l.end(), std::inserter(dest_list, dest_list.end()));

back_inserter

AND,与inserter不同,据我所知,std::vector适用于任何STL容器!!我成功地对std::liststd::mapstd::unordered_mappush_back进行了尝试,然后才惊讶地来到这里。

我认为这可能是因为insert(.end())对于某些结构来说可能比std::list更快,但我不确定...

// Copying 10,000,000 element-list with std::copy. Did it twice w/ switched order just in case that matters. Profiling complete (884.666 millis total run-time): inserter(.end()) Profiling complete (643.798 millis total run-time): back_inserter Profiling complete (644.060 millis total run-time): back_inserter Profiling complete (623.151 millis total run-time): inserter(.end()) 的情况似乎并非如此(有道理):

std::vector

但它对// Copying 10,000,000 element-vector with std::copy. Profiling complete (985.754 millis total run-time): inserter(.end()) Profiling complete (746.819 millis total run-time): back_inserter Profiling complete (745.476 millis total run-time): back_inserter Profiling complete (739.774 millis total run-time): inserter(.end()) 略有影响,虽然我不确定为什么?:

std::inserter(container, container.end())

我猜在向量中有更多的开销来计算迭代器的位置,然后在那里放置一个元素而不仅仅是arr [count ++]。也许就是这样?

但是,这仍然是主要原因吗?

我的后续问题,我猜是"是否可以为模板化函数编写g++ -O0 -std=c++11 algo_test.cc 并期望它适用于(几乎)任何STL容器?"


我转移到标准编译器后更新了数字。这是我的编译器的详细信息:
gcc版本4.8.2(Ubuntu 4.8.2-19ubuntu1)
目标:x86_64-linux-gnu

我的构建命令:

std::inserter(container, container.end())

我认为this question asks the second half of my question,即"我可以编写一个使用std::forward_list的模板化函数,并期望它几乎适用于每个容器吗?"

答案是"是的,除了std::vector之外的每个容器。"但根据以下评论和user2746253的答案中的讨论,听起来我应该知道std::back_inserter比使用RandomAccessIterator要慢一些。

因此,我可能希望使用back_inserter来专门化我的容器模板以使用{{1}}。那有意义吗?感谢。

2 个答案:

答案 0 :(得分:52)

迭代器类型

  • std::back_inserter返回使用std::back_insert_iterator。{/ li>的Container::push_back()
  • std::inserter返回使用std::insert_iterator。{/ li>的Container::insert()

的std ::列表

对于列表std::list::push_back几乎与std::list::insert相同。唯一的区别是insert将迭代器返回到插入的元素。

位/ stl_list.h

void push_back(const value_type& __x)
  { this->_M_insert(end(), __x); }
void _M_insert(iterator __position, const value_type& __x)
  {
    _Node* __tmp = _M_create_node(__x);
    __tmp->_M_hook(__position._M_node);
  }

位/ list.tcc

template<typename _Tp, typename _Alloc> typename list<_Tp, _Alloc>::iterator
list<_Tp, _Alloc>::insert(iterator __position, const value_type& __x)
  {
  _Node* __tmp = _M_create_node(__x);
  __tmp->_M_hook(__position._M_node);
  return iterator(__tmp);
  }

的std ::矢量

std::vector看起来有点不同。如果需要重新分配,则推回检查,如果不是,则将值放在正确的位置。

位/ stl_vector.h

void push_back(const value_type& __x)
  {
  if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
    {
    _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
    ++this->_M_impl._M_finish;
    }
  else
    _M_insert_aux(end(), __x);
  }

但是在std::vector::insert中,还有3件事情已经完成,它会影响性能。 比特/ vector.tcc

template<typename _Tp, typename _Alloc> typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::insert(iterator __position, const value_type& __x)
  {
  const size_type __n = __position - begin(); //(1)
  if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage
  && __position == end()) //(2)
    {
    _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
    ++this->_M_impl._M_finish;
    }
  else
    {
    _M_insert_aux(__position, __x);
    }
  return iterator(this->_M_impl._M_start + __n); //(3)
  }

答案 1 :(得分:0)

简短回答是std::insert_iterator允许您插入容器中的任何位置:

//insert at index 2
auto it = std::inserter(v, v.begin() + 2);
*it = 4;

要实现这一点,std :: vector必须在上面的示例中将索引2之后的现有元素向下移动一个位置。这是O(n)操作,除非您在最后插入,因为没有其他任何东西可以向下移动。但仍需要进行相关检查,导致O(1)性能惩罚。对于链接列表,您可以在O(1)时间内的任何地方插入,因此不会受到任何惩罚。 back_inserter总是在末尾插入,所以也没有任何惩罚。