glibc中stl list::size()实现的延伸问题

STL glibc list的size方法
所以仅仅是判断list容器是否有元素存在应该使用empty而不是list,避免进入复杂度为O(N)的重载函数中导致入坑

bool empty() const { return _M_node->_M_next == _M_node; }

  size_type size() const {
    size_type __result = 0;
    distance(begin(), end(), __result);
    return __result;
  }

template<class InputIterator>
inline typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last) {
	typedef typename 
		iterator_traits<Iterator>::iterator_category category;
	return __distance(first, last, category());
}

//__distance函数根据迭代器类型的不同版本,效率也不尽相同 
template<class InputIterator>
inline typename iterator_traits<InputIterator>::difference_type
__distance(InputIterator first, InputIterator last,
	input_iterator_tag) {
	iterator_traits<InputIterator>::difference_type n = 0;
	//遍历 复杂度O(N)
	while (first != last) {
		++first; ++n;
	}
	return n;
}
 
template<class RandomAccessIterator>
inline typename iterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last,
	random_access_iterator_tag) {
	//头尾差值 复杂度O(1)
	return last - first;


void splice(iterator __position, list& __x) {
    if (!__x.empty()) 
      this->transfer(__position, __x.begin(), __x.end());
  }
  void splice(iterator __position, list&, iterator __i) {
    iterator __j = __i;
    ++__j;
    if (__position == __i || __position == __j) return;
    this->transfer(__position, __i, __j);
  }
  void splice(iterator __position, list&, iterator __first, iterator __last) {
    if (__first != __last) 
      this->transfer(__position, __first, __last);
  }
}

  void transfer(iterator __position, iterator __first, iterator __last) {
    if (__position != __last) {
      // Remove [first, last) from its old position.
      __last._M_node->_M_prev->_M_next     = __position._M_node;
      __first._M_node->_M_prev->_M_next    = __last._M_node;
      __position._M_node->_M_prev->_M_next = __first._M_node; 

      // Splice [first, last) into its new position.
      _List_node_base* __tmp      = __position._M_node->_M_prev;
      __position._M_node->_M_prev = __last._M_node->_M_prev;
      __last._M_node->_M_prev     = __first._M_node->_M_prev; 
      __first._M_node->_M_prev    = __tmp;
    }
  }

配合下面这张图更好理解transfer操作
glibc中stl list::size()实现的延伸问题
如果使用成员变量维护的话,需要去遍历迭代器拼接区间元素数量从而使得transfer复杂度变高了。如果size()使用遍历的方法去获取元素数量,那么transfer操作则不需要关心list元素个数变化。以此牺牲了size()方法的效率

不过C++11已经是对于size()方法维护了一个成员变量

图片来源<<stl源码剖析>>,代码来源github stl源码

上一篇:RTSP协议详解


下一篇:P7073 表达式 题解