34

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?

nikolas
  • 8,707
  • 9
  • 50
  • 70

3 Answers3

73

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.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
4

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;

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
-2

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.

Jan Herrmann
  • 2,717
  • 17
  • 21
  • Only the `enable_shared_from_this` base class needs to be public. – Jonathan Wakely Jul 25 '13 at 10:04
  • @JonathanWakely may be, but its not my task to develop this code. I've only shown more problems. As long as the component is not in production code and interfaces may change it may be easier to make everything public (as I have written). Of course at the end one should think about restrictive member access. But while developing, it can produce more errors like in this example. – Jan Herrmann Jul 25 '13 at 10:14
  • 3
    I still think breaking encapsulation is not the way to go, even when not in production - I'd rather know potential problems with encapsulation beforehand, before publishing an interface and then notice I'm struggling all around. – nikolas Jul 25 '13 at 12:13