2013-07-25 5 views
16

Mam menedżera zasobów, który podobnie jak Andrei Alexandrescu zaproponowany w książce Modern C++ Design, jest oparty na zasadach. Mam jednak problem, ponieważ mój menedżer zasobów musi być w stanie przekazać sobie odniesienia do zarządzanych zasobów przez shared_from_this().Używanie shared_from_this w klasach szablonowych

Zbudowałem minimalny przykład odtwarzający mój problem, którego wyniki można zobaczyć here.

Zasadniczo mam niektóre udało zasobem, który potrzebuje odniesienie do jego zarządcy:

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

i kierownik, który przechowuje i udostępnia zasoby:

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

Jak widać, przechowywania sobie jest oparte na polityce. Podczas gdy menedżer tworzy zasoby, polityka może dowolnie decydować pomiędzy różnymi podejściami do przechowywania informacji (może np. Zdecydować, że nie będzie niczego przechowywać i tworzyć nowe zasoby za każdym razem).

Jest to przykład polityki magazynowej:

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

Ale pojawia się błąd kompilacji:

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

do pełnego wyjścia kompilacji zobaczyć minimal example.

Czy korzystanie z std::enable_shared_from_this i shared_from_this() jest niemożliwe w ramach projektu opartego na zasadach? Jeśli nie, jaki jest właściwy sposób korzystania z niego?

+0

manager_ptr const & parent? –

Odpowiedz

33

enable_shared_from_this<manager<Policy>> jest „baza zależny” (jest to klasa bazowa, których rodzaj zależy od parametru szablonu, w tym przypadku Policy) więc zasady C++ twierdzą, że bez zastrzeżeń wyszukiwanie nazw nie spojrzeć tam, trzeba powiedzieć, this->shared_from_this() lub std::enable_shared_from_this<manage<Policy>>::shared_from_this(), aby znaleźć członka z podległej bazy.

Aby uzyskać więcej szczegółowych informacji i linki do innych odniesień, zobacz stronę http://gcc.gnu.org/wiki/VerboseDiagnostics#dependent_base.

Aby rozwiązać ten drugi błąd, czego potrzeba, aby enable_shared_from_this się publicznego klasę bazową, albo nie może się inicjowany kiedy zarządca jest własnością shared_ptr.

+0

Podane odniesienie było ładne –

4

Kompilator mówi, że problem polega na wyszukiwaniu nazw zależnych a wyszukiwaniu nazw niezależnych. "Zależny" oznacza "zależy od parametru szablonu".

Nazwy niezależne są wyszukiwane, gdy definicja szablonu jest (najpierw) analizowana, a nazwy zależne (i ich elementy) są wyszukiwane dopiero po utworzeniu instancji szablonu.

W twoim przypadku nazwa shared_from_this nie zależy od parametrów szablonu, więc kompilator chce uzyskać do niego dostęp podczas analizowania szablonu. Jednak twoja klasa dostaje ją od enable_shared_from_this<manager<Policy>>, która ma wartość zależną od parametru szablonu, a więc jest analizowana tylko w czasie tworzenia instancji.

Musisz zmienić shared_from_this na nazwę zależną.Masz dwie opcje:

  1. Zakwalifikuj to, używając czegoś zależnego. Najłatwiej jest użyć this->shared_from_this().

  2. dostosowania go do zakresu jawnie, umieszczając using-deklaracji do definicji klasy: using std::enable_shared_from_this<manager<Policy>>::shared_from_this;

-1

Jak inni napisali trzeba użyć this->shared_from_this(). Ale nie jest to realy help. Jeszcze dokładniej edytowałem Twój kod i upubliczniłem wszystko (wszystkie classes jako structs i nie public, private, ...). Teraz to compiles. Czasami lepiej jest myśleć o ograniczaniu dostępu do członków podczas wykonywania prototypów (ponieważ może to spowodować więcej błędów kompilacji). Można to zrobić później, gdy testy będą w porządku.

+0

Tylko klasa podstawowa 'enable_shared_from_this' musi być publiczna. –

+0

@ JonathanWakely może być, ale nie jest moim zadaniem rozwijanie tego kodu. Pokazałem tylko więcej problemów. Dopóki komponent nie jest w kodzie produkcyjnym, a interfejsy mogą się zmieniać, łatwiejsze może być upublicznienie wszystkiego (jak pisałem). Oczywiście na koniec należy pomyśleć o restrykcyjnym dostępie członków. Ale podczas rozwijania może powodować więcej błędów, jak w tym przykładzie. –

+3

Nadal uważam, że przełamanie enkapsulacji nie jest rozwiązaniem, nawet jeśli nie jest produkowane - wolałbym wcześniej znać potencjalne problemy z enkapsulacją, zanim opublikuję interfejs, a potem zauważę, że walczę dookoła. – nijansen