Использование 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
,...). Теперь он компилирует. Иногда лучше не думать о ограничении доступа к членам при выполнении прототипирования (поскольку это может привести к большим ошибкам компиляции). Это можно сделать позже, когда тесты будут в порядке.