std :: unique_ptr НЕ является нулевой стоимостью - PullRequest
2 голосов
/ 04 ноября 2019

У меня есть настройка, похожая на эту:

Существует класс, аналогичный vector (он реализован с использованием std :: vector).

Содержит указатели на int.

Я использую свой собственный распределитель.

Вектор не создает элементов, но он может уничтожать элементы.

Чтобы уничтожить его, необходимо вызвать нестатический метод Allocator::deallocate(int *p).

Если я делаю это с ручным управлением в реальном времени, я могу позвонить Allocator::deallocate(int *p) вручную. Это работает, но не RAII.

В качестве альтернативы, я могу использовать std::unique_ptr с пользовательским удалением. Однако, если я это сделаю, размер массива станет двойным, потому что каждый std::unique_ptr должен содержать указатель на распределитель.

Есть ли способ сделать это без удвоения размера вектора?

Заметьте, я не хочу шаблонизировать класс.

Вот лучший код RAII, который я приду.

#include <functional>
#include <cstdlib>
#include <memory>



struct MallocAllocator{
    template<class T>
    static T *allocate(size_t size = sizeof(T) ) noexcept{
        return reinterpret_cast<T *>( malloc(size) );
    }

    // this is deliberately not static method
    void deallocate(void *p) noexcept{
        return ::free(p);
    }

    // this is deliberately not static method
    auto getDeallocate() noexcept{
        return [this](void *p){
            deallocate(p);
        };
    }
};



struct S{
    std::function<void(void *)> fn;

    S(std::function<void(void *)> fn) : fn(fn){}

    auto operator()() const{
        auto f = [this](void *p){
            fn(p);
        };

        return std::unique_ptr<int, decltype(f)>{ (int *) malloc(sizeof(int)), f };
    }
};



int main(){
    MallocAllocator m;

    S s{ m.getDeallocate() };

    auto x = s();

    printf("%zu\n", sizeof(x));
}

1 Ответ

3 голосов
/ 04 ноября 2019

Вы не можете сделать это. Если вы хотите, чтобы ваш unique_ptr сохранял ссылку на нестатическое удаление, вы ничего не можете с этим поделать, ему придется где-то его хранить.

Некоторые способы обойти это:

  1. Если вы используете структуру данных с учетом распределителя, передайте ей свой распределитель и не используйте unique_ptr, используйте фактический тип данных как сохраненный тип.
  2. Оберните васвыделенные объекты вокруг своего рода менеджера, который будет освобождать эти объекты при необходимости. Вы теряете RAII внутри него, но для внешнего кода это все равно будет RAII. Вы даже можете передать право владения некоторыми объектами из этого менеджера во внешний код, и вам нужно будет использовать там только свое собственное средство удаления.
  3. (не рекомендуется) Используйте некоторое глобальное состояние, к которому вы можете обратиться из средства удаления, чтобы сделатьэто размер 0.
...