Предоставляет ли 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.