Почему unique_ptr не предотвращает нарезку пользовательского удалителя? - PullRequest
4 голосов
/ 25 мая 2019

Поведение std::unique_ptr с custom deleter основано на статическом типе средства удаления. Отсутствие полиморфизма, никакого поведения во время выполнения, основанного на фактическом удаленном средстве, переданном во время выполнения, поскольку предоставленный производный удалитель подразделяется на статический тип объявленного удалителя.

(Он предназначен специально для того, чтобы размер unique_ptr с default deleter или с custom deleter without any data members имел тот же размер, что и необработанный указатель).

статическое поведение unique_ptr с custom deleter:

class A {};

struct BaseDeleter {
    virtual void operator()(A* p) const {
        std::cout << "in BaseDeleter" << std::endl; 
        delete p;
    }
};

struct DerivedDeleter: BaseDeleter {
    void operator()(A* p) const override {
        std::cout << "in DerivedDeleter" << std::endl; 
        delete p;
    }
};

int main() {
    auto unique_var = std::unique_ptr<A, BaseDeleter>(new A);
    unique_var = std::unique_ptr<A, DerivedDeleter>(new A);
}

Вывод:

in BaseDeleter
in BaseDeleter

Это в отличие от std::shared_ptr, который по-своему хранит свое собственное средство удаления и допускает динамическое поведение:

динамическое поведение shared_ptr с custom deleter:

int main() {
    auto shared_var = std::shared_ptr<A>(new A, BaseDeleter{});
    shared_var = std::shared_ptr<A>(new A, DerivedDeleter{});
}

Выход:

in BaseDeleter
in DerivedDeleter

Код: https://coliru.stacked -crooked.com / a / 54a8d2fc3c95d4c1


Поведение присваивания std::unique_ptr с разными custom deleter на самом деле нарезка .

Почему unique_ptr не предотвращает нарезку пользовательского удалителя?

Почему язык не заблокировал присвоение std::unique_ptr, если назначенный unique_ptr имеет другое custom deleter, чтобы избежать нарезки?


Это представляется возможным, как показано ниже.

Блокировка unique_ptr от нарезки custom deleter

template<typename TYPE, typename Deleter>
struct my_unique_ptr : std::unique_ptr<TYPE, Deleter> {
    using BASE = std::unique_ptr<TYPE, Deleter>;
    using std::unique_ptr<TYPE, Deleter>::unique_ptr;
    auto& operator=(std::nullptr_t) noexcept {
        return BASE::operator=(nullptr);
    }
    template<typename T, typename OtherDeleter,
      std::enable_if_t<!std::is_same<OtherDeleter, Deleter>::value>* dummy = nullptr>
    auto& operator=(std::unique_ptr<T, OtherDeleter>&& other) = delete;
};

Код: http://coliru.stacked -crooked.com / a / 089cd4c7303ad63e

1 Ответ

2 голосов
/ 25 мая 2019
struct B {
  virtual ~B() = default;
};

struct D : B {};

std::unique_ptr<B> b;
b = std::make_unique<D>();

Здесь мы имеем классический вариант использования.Да, удаляется разделитель, но удаление все еще четко определено.Ваше предложение будет мешать этому.И, вероятно, будет очень трудно надежно изменить его, чтобы он не мешал.

Всегда можно указать пользовательское средство удаления, например std::function<void(void*)>, чтобы получить полиморфизм при стирании типа.Конечно, у него есть накладные расходы, но он включен.

По умолчанию unique_ptr оптимизирован для более распространенных случаев использования, причем менее распространенные, требующие накладных расходов, возможны по запросу.

...