web-dev-qa-db-ja.com

テンプレート化されたクラスでshared_from_thisを使用する

私には、 『Modern C++ Design』で提案されているAndrei Alexandrescuのように、ポリシーベースの設計に従うリソースマネージャーがいます。ただし、リソースマネージャーは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()を使用することは不可能ですか?そうでない場合、それを使用する適切な方法は何ですか?

22
nijansen

_enable_shared_from_this<manager<Policy>>_は「依存ベース」(タイプがテンプレートパラメーターに依存する基本クラス、この場合はPolicy)であるため、C++のルールでは、非修飾名ルックアップはそこを検索しないとされています。 、依存ベースからメンバーを見つけるには、this->shared_from_this()またはstd::enable_shared_from_this<manage<Policy>>::shared_from_this()と言う必要があります。

詳細および他のリファレンスへのリンクについては、 http://gcc.gnu.org/wiki/VerboseDiagnostics#dependent_base を参照してください。

2番目のエラーを修正するには、_enable_shared_from_this_をpublic基本クラスにする必要があります。そうしないと、マネージャーがによって所有されている場合に初期化できません。 _shared_ptr_。

50
Jonathan Wakely

コンパイラーは、問題が従属名ルックアップと非従属名ルックアップであると言っています。 「依存」とは、「テンプレートパラメータに依存する」ことを意味します。

非依存名は、テンプレート定義が(最初に)解析されるときに検索されますが、依存名(およびそのメンバー)は、テンプレートがインスタンス化されるときにのみ検索されます。

あなたの場合、名前_shared_from_this_はテンプレートパラメータに依存しないため、コンパイラはテンプレートを解析するときにそれにアクセスしたいと考えています。ただし、クラスは_enable_shared_from_this<manager<Policy>>_から取得します。これはdoesテンプレートパラメータに依存するため、インスタンス化時にのみ調べられます。

_shared_from_this_を従属名に変換する必要があります。 2つのオプションがあります。

  1. 依存するものでそれを修飾します。最も簡単なのはthis->shared_from_this()を使用することです。

  2. クラス定義にusing宣言を入れて、明示的にスコープに入れます:_using std::enable_shared_from_this<manager<Policy>>::shared_from_this;_