Использование shared_from_this в шаблонных классах

У меня есть менеджер ресурсов, который, как предложил Андрей Александреску в книге Modern С++ Design, следует за политическим дизайном. У меня проблемы, потому что мой менеджер ресурсов должен иметь возможность предоставлять ссылки на управляемые ресурсы с помощью shared_from_this().

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

В основном у меня есть управляемый ресурс, которому нужна ссылка на его менеджера:

template <typename T>
class managed_resource
{
        typedef std::shared_ptr<manager<T>> manager_ptr;
    public:
        managed_resource(manager_ptr const & parent)
            : parent_(parent)
        {
        }

        /* ... */

    private:
        manager_ptr parent_;
};

И менеджер, который хранит и предоставляет ресурсы:

template <typename Policy>
class manager
    : Policy
    , std::enable_shared_from_this<manager<Policy>>
{
        typedef managed_resource<Policy> resource;
        typedef std::shared_ptr<resource> resource_ptr;
    public:
        resource_ptr get_resource(std::string const & name)
        {
            Policy & p = *this;
            if(p.find(name))
            {
                return p.get(name);
            }
            resource_ptr res = std::make_shared<resource>(shared_from_this());
            p.store(name, res);
            return res;
        }
};

Как вы можете видеть, хранение является политическим. В то время как менеджер создает ресурсы, политика может свободно выбирать между различными подходами к хранению информации (она может, например, выбирать что-то не хранить и создавать новые ресурсы каждый раз).

Это пример политики хранения:

class map_policy
{
        typedef std::shared_ptr<managed_resource<map_policy>> resource_ptr;
        typedef std::map<std::string, resource_ptr> resources;

    public:
        bool find(std::string const & name)
        {
            resources::iterator res_it = resources_.find(name);
            return res_it != resources_.end();
        }

        resource_ptr get(std::string const & name)
        {
            resources::iterator res_it = resources_.find(name);
            return res_it->second;
        }

        void store(std::string const & name, resource_ptr const & res)
        {
            resources_[name] = res;
        }

    private:
        resources resources_;
};

Но я получаю ошибку компиляции:

error: there are no arguments to ‘shared_from_this’ that depend 
       on a template parameter, so a declaration of 
       ‘shared_from_this’ must be available
error: ‘std::enable_shared_from_this<manager<map_policy> >’ is 
       an inaccessible base of ‘manager<map_policy>’

Полный вывод компиляции приведен в минимальном примере.

Нельзя ли использовать std::enable_shared_from_this и shared_from_this() в рамках разработки на основе политик? Если нет, каков его правильный способ?

Ответы

Ответ 1

enable_shared_from_this<manager<Policy>> является "зависимой базой" (это базовый класс, тип которого зависит от параметра шаблона, в данном случае Policy), поэтому правила С++ говорят, что поиск неквалифицированного имени не выглядит там, вы нужно сказать this->shared_from_this() или std::enable_shared_from_this<manage<Policy>>::shared_from_this(), чтобы найти элемент из зависимой базы.

Подробнее см. http://gcc.gnu.org/wiki/VerboseDiagnostics#dependent_base и ссылки на другие ссылки.

Чтобы исправить вторую ошибку, вам нужно сделать enable_shared_from_this общедоступный базовый класс, или он не может быть инициализирован, когда менеджер принадлежит shared_ptr.

Ответ 2

Компилятор сообщает вам, что проблема связана с зависимым именем, а не с независимым именем. "Зависимый" означает "зависит от параметра шаблона".

Независимые имена просматриваются, когда определение шаблона (сначала) анализируется, а зависимые имена (и их члены) просматриваются только при создании экземпляра шаблона.

В вашем случае имя shared_from_this не зависит от каких-либо параметров шаблона, поэтому компилятор хочет получить к нему доступ при разборе шаблона. Тем не менее, ваш класс получает его от enable_shared_from_this<manager<Policy>>, который зависит от параметра шаблона и поэтому рассматривается только в момент создания экземпляра.

Вы должны включить shared_from_this в зависимое имя. У вас есть два варианта:

  • Откажитесь от него с чем-то зависимым. Самый простой способ - использовать this->shared_from_this().

  • Внесите его в область видимости явным образом, поместив в определение класса объявление-использование: using std::enable_shared_from_this<manager<Policy>>::shared_from_this;

Ответ 3

Как написано другими, вы должны использовать this->shared_from_this(). Но это не реальная помощь. Я отредактировал ваш код дальше и сделал все общедоступным (все classes как structs и no public, private,...). Теперь он компилирует. Иногда лучше не думать о ограничении доступа к членам при выполнении прототипирования (поскольку это может привести к большим ошибкам компиляции). Это можно сделать позже, когда тесты будут в порядке.