就目前而言,代码看起来不错。
我检查了libc++中的代码(相关部分),我相信它只会混淆静态分析器。
详细信息:
template <class _Tp, class _Alloc>
void list<_Tp, _Alloc>::pop_front()
{
_LIBCPP_ASSERT(!empty(), "list::pop_front() called with empty list");
__node_allocator& __na = base::__node_alloc();
__node_pointer __n = base::__end_.__next_;
base::__unlink_nodes(__n, __n);
--base::__sz();
__node_alloc_traits::destroy(__na, _VSTD::addressof(__n->__value_));
__node_alloc_traits::deallocate(__na, __n, 1);
}
list
被实现为一个循环列表,基于__end_
(这是结束指针),所以要到达第一个元素,代码转到__end_.__next_
.
的实现__unlink_nodes
是:
// Unlink nodes [__f, __l]
template <class _Tp, class _Alloc>
inline void __list_imp<_Tp, _Alloc>::__unlink_nodes(__node_pointer __f,
__node_pointer __l) noexcept
{
__f->__prev_->__next_ = __l->__next_;
__l->__next_->__prev_ = __f->__prev_;
}
我们可以通过一些简单的 ASCII 艺术很容易地理解它:
Z A B C
+---------+ +---------+ +---------+ +---------+
--| __prev_ |<--| __prev_ |<--| __prev_ |<--| __prev_ |<-
->| __next_ |-->| __next_ |-->| __next_ |-->| __next_ |--
+---------+ +---------+ +---------+ +---------+
要删除范围A
-B
从此列表中:
Z.__next_
必须指向C
C.__prev_
必须指向Z
因此,调用__unlink_nodes(A, B)
将:
- 取
A.__prev_.__next_
(ie, Z.__next_
) 并使其指向B.__next_
(ie, C
)
- 取
B.__next_.__prev_
(ie, C.__prev_
) 并使其指向A.__prev_
(ie, Z
)
这很简单,即使在使用单个元素范围(这里的情况)调用时也可以工作。
但是,现在请注意,如果list
是空的,这根本不起作用!的默认构造函数__list_node_base
是:
__list_node_base()
: __prev_(static_cast<pointer>(pointer_traits<__base_pointer>::pointer_to(*this))),
__next_(static_cast<pointer>(pointer_traits<__base_pointer>::pointer_to(*this)))
{}
也就是说,它指的是它自己。在这种情况下,__unlink_nodes
用&__end_
(两次)调用,并且不会改变它__end_.__prev_.__next_ = __end_.__next_
是幂等的(因为__end_.prev
它__end_
本身)。
可能是这样的:
- 分析器考虑了空列表的情况(
_LIBCPP_ASSERT
正在编译)
- 并得出结论,在这种情况下,
__end_.__next_
(used by begin()
) 被deallocate()
调用pop_front()
或者也许它是指针舞中的其他东西......希望 Clang 团队能够修补这些东西。