Код в его нынешнем виде выглядит нормально.
Я проверяю код из 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_
(т.е. Z.__next_
) и сделайте так, чтобы он указывал на B.__next_
(т.е. C
)
- возьмите
B.__next_.__prev_
(т.е. C.__prev_
) и сделайте так, чтобы он указывал на A.__prev_
(т.е. 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_
(используемый begin()
) остается висящим из-за вызова deallocate()
в pop_front()
Или, может быть, это что-то еще в танце указателей... надеюсь, команда Clang сможет все исправить.
person
Matthieu M.
schedule
09.05.2014
libc++
, но не при использовании GNUlibstdc++
(начиная сclang-3.5
иgcc-4.9
соответственно в 64-битном Debian), это вполне может быть ошибкой в libc++
. - person Massa   schedule 07.05.2014libstdc++
не предлагает поддержку С++ 11. Но я добавлю эту информацию в баг-репорт на Bugzilla. - person Thierry   schedule 08.05.2014