Как правильно использовать распределители для полиморфных типов? - PullRequest
0 голосов
/ 17 июня 2019

Я добавляю пользовательские распределители памяти в систему и пытаюсь сделать это правильно с помощью концепции Allocator, но я столкнулся с некоторой неопределенностью при достижении точки, где тип выделенного компонента был стерта.

class Pimpl_interface {
public:
  virtual ~Pimpl_interface() {}
  virtual void foo() = 0;
};

template<typename T>
class Pimpl : public Pimpl_interface {
public:
  void foo() override {}
};

template<typename Alloc = std::allocator<void>>
class MyType : public Alloc {
  Pimpl_interface * pimpl_ = nullptr;
public:  
  MyType(const Alloc& alloc) : Alloc(alloc) {}

  ~MyType() {
    if(pimpl_) {
      pimpl_->~Pimpl_interface();
      // What do I put here ?????
    }
  }

  template<typename T>
  void bar() {
    using real_alloc_t = typename Alloc::template rebind<Pimpl<T>>::other;
    real_alloc_t alloc(*static_cast<Alloc*>(this));
    Pimpl<T>* ptr = alloc.allocate(1);
    try {
      pimpl_ = new(&ptr) Pimpl<T>();
    }
    catch(...) {
      alloc.deallocate(ptr, 1);
      throw;
    }
  }
};

На первый взгляд кажется, что нужно сделать перепривязку Alloc к Pimpl_interface и надеяться, что это правильно. Я знаю, что это будет нормально работать с std::allocator, но должно ли это то, что пользовательские распределители должны поддерживать для всех типов перебираемых распределителей?

В худшем случае я могу вместо этого взять объект-распределитель типа 1010 *, подобный утке, но я бы лучше попытался следовать стандартной библиотеке, если смогу.

1 Ответ

0 голосов
/ 18 июня 2019

Я чувствую, что многое происходит. Похоже, что в основе вы хотите безопасно использовать альтернативные распределители (в частности, в классе PImpl'd). Для меня наименьшая полезная часть этого требования - заставить распределитель работать с std::unique_ptr. Если я буду следовать правильно, я думаю, вы захотите, чтобы это было std::unique_ptr<T, std::function<void(T*)>>. Точно так же, похоже, std::allocate_shared может быть достаточно близко к тому, что вы хотите. https://en.cppreference.com/w/cpp/memory/shared_ptr/allocate_shared

Это то, что тебе нужно?:

template<typename Alloc = std::allocator<void>>
class MyType {
  Alloc alloc_;
  std::shared_ptr<Pimpl_interface> pimpl_ = nullptr;
public:  
  MyType(const Alloc& alloc) : Alloc(alloc) {}

  ~MyType() {} // shared_ptr ftw.

  template<typename T>
  void bar() {
    using real_alloc_t = typename Alloc::template rebind<Pimpl<T>>::other;
    real_alloc_t alloc(alloc_);
    pimpl_ = std::allocate_shared<Pimpl<T>>(alloc);
  }
};

(Я уронил наследство с Alloc, потому что это казалось диким.)

...