Using shared_from_this in templated classes

[亡魂溺海] 提交于 2020-05-25 03:41:10

问题


I have a resource manager that, like Andrei Alexandrescu proposed in the book Modern C++ Design, follows a policy based design. I am having trouble though, because my resource manager needs to be able to provide references to itself to the managed resources by shared_from_this().

I built a minimal example reproducing my problem, which results you can see here.

Basically I have some managed resource that needs a reference to its manager:

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_;
};

And a manager that stores and provides resources:

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;
        }
};

As you can see, the storing itself is policy-based. While the manager does create the resources, the policy can freely decide between various approaches of storing the information (it could e.g. choose to not store anything and create new resources every time).

This is an example of a storage policy:

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_;
};

But I get a compilation error:

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>’

For full compilation output see the minimal example.

Is it impossible to use std::enable_shared_from_this and shared_from_this() within policy based design? If not, what is the proper way of using it?


回答1:


enable_shared_from_this<manager<Policy>> is a "dependent base" (it's a base class whose type depends on a template parameter, in this case Policy) so the rules of C++ say that unqualified name lookup doesn't look in there, you need to say this->shared_from_this() or std::enable_shared_from_this<manage<Policy>>::shared_from_this() to find the member from the dependent base.

See http://gcc.gnu.org/wiki/VerboseDiagnostics#dependent_base for more detail and links to other references.

To fix the second error you need to make enable_shared_from_this a public base class, or it can't get initialized when the manager is owned by a shared_ptr.




回答2:


The compiler is telling you the problem is dependent-name lookup vs. nondependent-name lookup. "Dependent" means "depends on a template parameter."

Nondependent names are looked up when the template definition is (first) parsed, while dependent names (and their members) are only looked up when the template is instantiated.

In your case, the name shared_from_this doesn't depend on any template parameters, so the compiler wants to access it when parsing the template. However, your class gets it from enable_shared_from_this<manager<Policy>>, which does depend on a template parameter, and so is only looked into at instantiation time.

You must turn shared_from_this into a dependent name. You have two options:

  1. Qualify it with something dependent. The easiest is using this->shared_from_this().

  2. Bring it into scope explicitly, by putting a using-declaration into your class definition: using std::enable_shared_from_this<manager<Policy>>::shared_from_this;




回答3:


As others have written you have to use this->shared_from_this(). But it doesn't realy help. I have edited your code further and made everything public (all classes as structs and no public, private, ...). Now it compiles. Sometimes it is better not to think about limiting access to members while doing prototyping (as it can result in more compilation errors). This can be done later when tests are ok.



来源:https://stackoverflow.com/questions/17853212/using-shared-from-this-in-templated-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!