Как установить объект, полученный из пула смарт-объектов в стиле RAII в C ++? - PullRequest
1 голос
/ 21 апреля 2020

Я использую пул смарт-объектов в стиле RAII и хочу установить полученный объект как член другого класса.

Пул смарт-объектов, предложенный Т. C. :

template <class T, class D = std::default_delete<T>>
class SmartObjectPool 
{
    private:
        struct ReturnToPool_Deleter {
            explicit ReturnToPool_Deleter(std::weak_ptr<SmartObjectPool<T, D>* > pool) : pool_(pool) {

            }

            void operator()(T* ptr) {
                if (auto pool_ptr = pool_.lock())
                    (*pool_ptr.get())->add(std::unique_ptr<T, D>{ptr});
                else
                    D{}(ptr);
            }
            private:
                std::weak_ptr<SmartObjectPool<T, D>* > pool_;
        };

    public:
        using ptr_type = std::unique_ptr<T, ReturnToPool_Deleter >;

        SmartObjectPool() : this_ptr_(new SmartObjectPool<T, D>*(this)) {}
        virtual ~SmartObjectPool(){}

        void add(std::unique_ptr<T, D> t) {
            pool_.push(std::move(t));
        }

        ptr_type acquire() {
            if (pool_.empty())
                throw std::out_of_range("Cannot acquire object from an empty pool.");

            ptr_type tmp(pool_.top().release(),
                            ReturnToPool_Deleter{
                            std::weak_ptr<SmartObjectPool<T, D>*>{this_ptr_}});
            pool_.pop();
            return std::move(tmp);
        }

        bool empty() const {
            return pool_.empty();
        }

        size_t size() const {
            return pool_.size();
        }

    private:
        std::shared_ptr<SmartObjectPool<T, D>* > this_ptr_;
        std::stack<std::unique_ptr<T, D> > pool_;
};

И класс Setter, для которого я хочу установить полученный объект как член другого класса (Setter):

class Setter {
    public:
        void setValue(SmartObjectPool<int>::ptr_type value) {
            m_value = std::move(value);
        }
    private:
        SmartObjectPool<int>::ptr_type m_value;
};

и основная реализация:

int main()
{

SmartObjectPool<int> pool;
pool.add(std::make_unique<int>());
assert(pool.size() == 1u);
{
  auto obj = pool.acquire();
  Setter setter;
  setter.setValue(std::move(obj));
  assert(pool.size() == 0u);
}
assert(pool.size() == 1u);
auto obj = pool.acquire();

return 0;

}

И я получил ошибку компиляции о конструкторе ReturnToPool_Deleter:

In file included from /usr/include/c++/5/functional:55:0,
                 from /usr/include/c++/5/memory:79,
                 from src/main.cpp:1:
/usr/include/c++/5/tuple: In instantiation of ‘constexpr std::_Head_base<_Idx, _Head, false>::_Head_base() [with long unsigned int _Idx = 1ul; _Head = SmartObjectPool<int>::ReturnToPool_Deleter]’:
/usr/include/c++/5/tuple:353:15:   required from ‘constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl() [with long unsigned int _Idx = 1ul; _Head = SmartObjectPool<int>::ReturnToPool_Deleter]’
/usr/include/c++/5/tuple:202:29:   required from ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl() [with long unsigned int _Idx = 0ul; _Head = int*; _Tail = {SmartObjectPool<int>::ReturnToPool_Deleter}]’
/usr/include/c++/5/tuple:602:20:   required from ‘constexpr std::tuple<_T1, _T2>::tuple() [with _T1 = int*; _T2 = SmartObjectPool<int>::ReturnToPool_Deleter]’
/usr/include/c++/5/bits/unique_ptr.h:158:14:   required from ‘constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr() [with _Tp = int; _Dp = SmartObjectPool<int>::ReturnToPool_Deleter]’
src/main.cpp:59:7:   required from here
/usr/include/c++/5/tuple:105:22: error: no matching function for call to ‘SmartObjectPool<int>::ReturnToPool_Deleter::ReturnToPool_Deleter()’
       : _M_head_impl() { }
                      ^
src/main.cpp:11:22: note: candidate: SmartObjectPool<T, D>::ReturnToPool_Deleter::ReturnToPool_Deleter(std::weak_ptr<SmartObjectPool<T, D>*>) [with T = int; D = std::default_delete<int>]
             explicit ReturnToPool_Deleter(std::weak_ptr<SmartObjectPool<T, D>* > pool)
                      ^
src/main.cpp:11:22: note:   candidate expects 1 argument, 0 provided
src/main.cpp:10:16: note: candidate: SmartObjectPool<int>::ReturnToPool_Deleter::ReturnToPool_Deleter(const SmartObjectPool<int>::ReturnToPool_Deleter&)
         struct ReturnToPool_Deleter {
                ^
src/main.cpp:10:16: note:   candidate expects 1 argument, 0 provided
src/main.cpp:10:16: note: candidate: SmartObjectPool<int>::ReturnToPool_Deleter::ReturnToPool_Deleter(SmartObjectPool<int>::ReturnToPool_Deleter&&)
src/main.cpp:10:16: note:   candidate expects 1 argument, 0 provided
src/main.cpp: In function ‘int main()’:
src/main.cpp:87:10: note: synthesized method ‘constexpr Setter::Setter()’ first required here
   Setter setter;
          ^
Makefile:17: recipe for target 'obj/main.o' failed
make: *** [obj/main.o] Error 1

Вопрос: Как я могу установить полученный объект как член другого класса?

1 Ответ

0 голосов
/ 21 апреля 2020

Проблема заключается в следующей строке

Setter setter;

Эта строка пытается создать по умолчанию Setter, но Setter не может быть построена по умолчанию. Конструктор по умолчанию Setter неявно удаляется, поскольку в нем есть элемент SmartObjectPool<int>::ptr_type, который сам по себе не конструируем.

SmartObjectPool<int>::ptr_type является псевдонимом std::unique_ptr<T, ReturnToPool_Deleter >. Теперь давайте посмотрим, что cppreference может сказать о конструкторе по умолчанию std::unique_ptr<T, Deleter>.

1) Создает std :: unique_ptr, которому ничего не принадлежит. Значение - инициализирует сохраненный указатель и сохраненный удалитель. Требует, чтобы Deleter был DefaultConstructible, и эта конструкция не выдает исключение.

Как видите, вы можете создать по умолчанию unique_ptr, только если Deleter является конструируемой по умолчанию, что в ваш случай не соответствует действительности: ReturnToPool_Deleter не является конструируемым по умолчанию.

Вместо того, чтобы делать то, что вы делаете сейчас, вы можете сделать что-то вроде этого:

using Deleter = std::function<void(T* ptr)>;
using ptr_type = std::unique_ptr<T, Deleter>;

Тогда

auto deleter = [pool_ = std::weak_ptr<SmartObjectPool<T, D>*>{this_ptr_} ](T* ptr) {
    if (auto pool_ptr = pool_.lock()) {
        (*pool_ptr.get())->add(std::unique_ptr<T, D>{ptr});
    } else {
        D{}(ptr);
    }
};

ptr_type tmp(pool_.top().release(), deleter);
...