В следующем коде я бы ожидал, что утверждение пройдет, но это не так.
Это отличается от документированного поведения unique_ptr :: reset, и я нахожу это довольно неожиданным.
Я что-то делаю неправильно или это ошибка? Это проблема, потому что, если один и тот же элемент удаляется с коэффициентом усиления, деструктор вызывается дважды.
#include <set>
#include <memory>
struct F
: std::enable_shared_from_this<F>
{
static int destructor_count;
static std::set<std::shared_ptr<F>> container;
F() {}
~F() {
assert(container.size() == 0);
container.clear(); // This will delete the same pointer twice.
destructor_count--;
}
};
int F::destructor_count = 0;
std::set<std::shared_ptr<F>> F::container;
int main()
{
F::container.insert(std::shared_ptr<F>(new F));
F::container.clear();
return 0;
}
Информация о компиляторе:
libstdС++ 6-4.6-DEV
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Там причина unique_ptr
явно документирована, чтобы вести себя таким образом: это более сильная гарантия, чем обычно для остальных стандартных библиотек. Если одно и то же правило применяется ко всем типам, то это не нужно указывать специально для unique_ptr
.
Ваш код рекурсивно вызывает F::container.clear()
пока выполняется другой вызов F::container.clear()
. Это не гарантируется:
17.6.5.8 Reentrancy [reentrancy]
За исключением случаев, явно указанных в этом стандарте, он определяется реализацией, функции которого в стандартной библиотеке С++ могут быть рекурсивно возвращены.
Теперь, к сожалению, libstdc++ не может документировать, какие функции могут быть рекурсивно возвращены, поэтому безопаснее предполагать, что никто не может быть, поэтому ваше утверждение неверно.
Я не уверен, почему вы ожидаете, что это утверждение будет истинным. Вы не должны знать информацию о том, как контейнеры хранят и уничтожают память. Вы не должны делать предположений о том, что происходит во время std::set::clear()
, только полагайте, что после завершения всех деструкторов будут вызваны, а std::set::size()
вернет 0.
В этой старой статье д-ра Доббса STL Red-Black Trees описана структура данных поддержки позади std::set
. Узлы будут удалены (и их содержимое уничтожено) до того, как все дерево будет пустым, но будьте уверены в конце std::set::clear()
все деструкторы будут вызваны, а std::set::size()
будет return 0.
Примечание: в других реализациях std::set
могут использоваться различные структуры данных резервного копирования, красно-черные деревья - это только возможная реализация.
Деструктор F вызывается при очистке контейнера, в течение которого контейнер не пуст, поэтому утверждение не выполнено.
unique_ptr
проход подтверждения?
std::make_shared
.