Требует ли dtor для shared_ptr использования «удалителя»?
Это широко известен что вы можете использоватьshared_ptr
хранить указатель на неполный тип, при условии, что указатель может быть удален (с четко определенным поведением) во время созданияshared_ptr
, Например, техника PIMPL:
struct interface
{
interface(); // out-of-line definition required
~interface() = default; // public ,inline member, even if implicitly defined
void foo();
private:
struct impl; // incomplete type
std::shared_ptr<impl> pimpl; // pointer to incomplete type
};
[Main.cpp]
int main()
{
interface i;
i.foo();
}
[Interface.cpp]
struct interface::impl
{
void foo()
{
std::cout << "woof!\n";
}
};
interface::interface()
: pimpl( new impl ) // `delete impl` is well-formed at this point
{}
void interface::foo()
{
pimpl->foo();
}
Это работает как"объект удаления" «Объект-владелец» (*) создается во время строительстваshared_ptr
вpimpl( new impl )
и сохраняется после стирания типа внутриshared_ptr
, Этот «объект-владелец» позже используется для уничтожения указанного объекта. Вот почему это должно быть безопасно, чтобы обеспечитьв линию деструкторinterface
.
Вопрос: Где Стандарт гарантирует, что это безопасно?
(*) Не удалитель в терминах Стандарта, см. Ниже, но он либо вызывает пользовательский удалитель, либо вызывает выражение удаления. Этот объект обычно хранится как часть объекта бухгалтерии, применяя стирание типа и вызывая пользовательское выражение удаления / удаления в виртуальной функции. На этом этапе выражение удаления также должно быть правильно сформировано.
Ссылаясь на последний черновик в репозитории github (94c8fc71, пересмотр N3797), [util.smartptr.shared.const]
template<class Y> explicit shared_ptr(Y* p);
3 Требуется:p
должен быть конвертируемым вT*
. Y
должен быть полным типом. Выражениеdelete p
должны быть правильно сформированы, иметь четко определенное поведение и не создавать исключений.
4 Эффекта: Создаетshared_ptr
объект, которому принадлежит указательp
.
5 постусловий:use_count() == 1 && get() == p
.
6 бросков:bad_alloc
или определяемое реализацией исключение, когда не удалось получить ресурс, отличный от памяти.
Примечание: Для этого ctor,shared_ptr
не требуется владеть удалителем, ПоDeleterСтандарт, кажется, означаетпользовательский удалительтакие, как вы предоставляете во время построения в качестве дополнительного параметра (илиshared_ptr
приобретает / делится один от другогоshared_ptr
например, через копирование-назначение). Также см. (Также см. [Util.smartptr.shared.const] / 9). Реализации (boost, libstdc ++, MSVC и, я думаю, каждая нормальная реализация) всегда хранят «объект-владелец».
КакDeleter этопользовательский удалительДеструкторshared_ptr
определяется с точки зренияdelete
(delete-expression), если пользовательского удалителя нет:
[Util.smartptr.shared.dest]
~shared_ptr();
1 Эффекты:
Если*this
являетсяпустой или делится собственностью с другимshared_ptr
экземпляр (use_count() > 1
), побочных эффектов нет.В противном случае, если*this
владеет объектp
и удалительd
, d(p)
называется.В противном случае,*this
владеет указательp
, а такжеdelete p
называется.Я возьму на себянамерение является то, что реализация требуется правильно удалить сохраненный указатель, даже если в области действияshared_ptr
dtor, выражение delete неправильно сформировано или вызовет UB. (Выражение delete должно быть правильно сформировано и иметь четкое поведение в ctor.) Итак, вопрос в том,
Вопрос: Где это требуется?
(Или я просто слишком придирчив, и как-то очевидно, что реализации должны использовать «объект-владелец»?)