Предоставляет ли shared_ptr свой объект при вызове делетера?
У меня есть std::shared_ptr
с пользовательским делетером, и в этом деле я хотел бы взять временную копию оригинала std::shared_ptr
. Выражается в виде кода:
struct Foo : public std::enable_shared_from_this<Foo>
{};
void deleter(Foo *f)
{
{
std::shared_ptr<Foo> tmp = f->shared_from_this(); // Line A
}
delete f;
}
int main()
{
std::shared_ptr<Foo> foo(new Foo, &deleter);
}
Мой вопрос: на линии А, можно ли что-нибудь сказать о вызове shared_from_this()
? Это законно? Если да, то стандарт говорит что-либо о его возвращаемом значении? Если мы заменим enable_shared_from_this
на другую weak_ptr
или глобальную ссылку на foo
, ответ будет таким же?
Clang с libС++ и gcc с libstdС++ оба производят код, который заканчивается на исключении bad_weak_ptr
, но я не могу проследить его, как того требует стандарт. Является ли это конкретной реализацией, или я пропустил правило?
Все соответствующие правила, которые я нашел (цитируя С++ 11):
20.7.2.2.2 shared_ptr
деструктор
1... если *this
принадлежит объект p
, а deleter d
, d(p)
вызывается 2 [Примечание:... Поскольку уничтожение *this
уменьшает количество экземпляров, которые разделяют право собственности на *this
одним, после того, как *this
был уничтожен все экземпляры shared_ptr
, которые разделяют право собственности на *this
, будут сообщите a use_count()
, что на один меньше его предыдущего значения. -end note]
20.7.2.2.5 shared_ptr
наблюдатели
7 use_count
Возвращает: количество объектов shared_ptr
, *this
включено, которые разделяют право собственности на *this
, или 0 когда *this
пусто.
Мне кажется, что неясно, происходит ли декремент use_count
до или после вызова делетера. Получает bad_weak_ptr
надежный результат, или это просто неуказано?
Обратите внимание, что я намеренно избегаю ситуации, когда указатель типа tmp
в моем примере кода переживет выполнение удаления.
Ответы
Ответ 1
рассмотрим
[С++ 14-12.4-15] Как только деструктор вызывается для объекта, объект больше не существует;
и
[С++ 14-20.8.2.4-7] shared_from_this() [...] Требуется: enable_shared_from_this должен быть доступным базовым классом T. * это будет подобъектом объект t типа T. Должен быть хотя бы один экземпляр shared_ptr p, которому принадлежит & t.
поэтому, учитывая, что делектор вызывается деструктором shared_ptr, вызывая shared_from_this() в deleter последнего владельца shared_ptr, он приводит к поведению undefined.
EDIT: как указано YSC, в С++ 17 shared_from_this() требуется вести себя как соответствующий вызов преобразования weak_ptr. Это усложняет ситуацию, потому что неясно, что weak_ptr:: expired() должен возвращаться при удачном вызове... в любом случае, принимая буквенную букву 20.7.2.2.2
в буквальном смысле, в этом случае должен быть поднят bad_weak_ptr.