Czy dtor shared_ptr wymaga użycia „deleter”?

Jego szeroko znany że możesz użyć ashared_ptr aby zapisać wskaźnik do niekompletnego typu, o ile wskaźnik może zostać usunięty (z dobrze zdefiniowanym zachowaniem) podczas budowyshared_ptr. Na przykład technika 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();
}

To działa jak„usuń obiekt” „obiekt właściciela” (*) jest tworzony podczas budowyshared_ptr wpimpl( new impl )i przechowywane po wymazaniu typu wewnątrzshared_ptr. Ten „obiekt właściciela” jest później używany do niszczenia wskazywanego obiektu. Dlatego zapewnienie bezpieczeństwa powinno być bezpiecznew linii destruktorinterface.

Pytanie: Gdzie Standard gwarantuje, że jest bezpieczny?

(*) Nie jest to błąd, jeśli chodzi o standard, patrz poniżej, ale wywołuje niestandardowy deleter lub wywołuje wyrażenie delete. Ten obiekt jest zazwyczaj przechowywany jako część obiektu księgowego, stosując wymazywanie typu i wywołując niestandardowe wyrażenie deleter / delete w funkcji wirtualnej. W tym momencie wyrażenie usuwania powinno być również dobrze ukształtowane.

Odwołując się do najnowszej wersji w repozytorium github (94c8fc71, wersja N3797), [util.smartptr.shared.const]

template<class Y> explicit shared_ptr(Y* p);

3 Wymaga:p jest wymienialny naT*. Y powinien być kompletny. Ekspresjadelete p będzie dobrze uformowany, będzie miał dobrze określone zachowanie i nie będzie rzucał wyjątków.

4 Efekty: Konstruuje ashared_ptr obiekt, który jest właścicielem wskaźnikap.

5 Postconditions:use_count() == 1 && get() == p.

6 rzutów:bad_alloclub wyjątek zdefiniowany przez implementację, gdy nie można uzyskać zasobu innego niż pamięć.

Uwaga: Dla tego ctorshared_ptr nie musi posiadać deletera. Przezdeleter, Standard wydaje się znaczyćniestandardowy deleter, tak jak podajesz podczas budowy jako dodatkowy parametr (lubshared_ptr nabywa / dzieli się jedna od drugiejshared_ptr, np. poprzez cesję). Zobacz także (zobacz także [util.smartptr.shared.const] / 9). Implementacje (boost, libstdc ++, MSVC i chyba każda implementacja sensowna) zawsze przechowują „obiekt właściciela”.

Jakdeleter jestniestandardowy deleter, destruktorshared_ptr jest zdefiniowany w kategoriachdelete (delete-expression), jeśli nie ma niestandardowego deletera:

[util.smartptr.shared.dest]

~shared_ptr();

1 Efekty:

Jeśli*this jestpusty lub dzieli się własnością z innymshared_ptr instancja (use_count() > 1), nie ma skutków ubocznych.W przeciwnym razie, jeśli*this jest właścicielem obiektp i deleterd, d(p) jest nazywany.Inaczej,*this jest właścicielem wskaźnikp, idelete p jest nazywany.

Przyjmęzamiar jest to, że implementacja jest wymagana do poprawnego usunięcia zapisanego wskaźnika, nawet jeśli w zakresieshared_ptr dtor, wyrażenie usuwające jest źle sformatowane lub wywołuje UB. (Wyrażenie delete musi być dobrze ukształtowane i mieć dobrze zdefiniowane zachowanie w ctor.) Więc pytanie brzmi

Pytanie: Gdzie to jest wymagane?

(A może jestem po prostu zbyt wybredny i oczywiste jest, że implementacje muszą używać „obiektu właściciela”?)

questionAnswers(1)

yourAnswerToTheQuestion