2017-02-13 109 views
2

我想爲各種類層次結構提供適當的克隆機器。看起來像一個合理的想法,我用CRTP拼湊了一個基本的解決方案,以在派生類中實現必要的clone()函數。可以以某種方式公開模板模板參數嗎?

我再與模板的模板參數模板它允許政策來控制克隆的存儲/持股比例:

template <typename base, typename derived> 
    struct clone_raw 
    { 
     typedef derived * return_type; 
     static return_type clone(const base * original) { return new derived(static_cast<const derived &>(*original)); } 
    }; 

    template <typename base, typename derived> 
    struct clone_shared 
    { 
     typedef std::shared_ptr<derived> return_type; 
     static return_type clone(const base * original) { return std::make_shared<derived>(static_cast<const derived &>(*original)); } 
    }; 

    template <typename base, typename derived> 
    struct clone_unique 
    { 
     typedef std::unique_ptr<derived> return_type; 
     static return_type clone(const base * original) { return std::make_unique<derived>(static_cast<const derived &>(*original)); } 
    }; 

    // derived class CRTP without base CRTP helper 
    template <typename base, typename derived, template <typename,typename> typename policy = clone_raw> 
    class clonable : public base 
    { 
    public: 
     // define our derived's parent class 
     using parent = clonable<base, derived, policy>; 

     // constructor forwarding (enable all constructors) 
     using base::base; 

     // clone using policy 
     auto clone() const 
     { 
      return policy<base, derived>::clone(this); 
     } 
    }; 

這工作得相當好,每一個派生類需要使用CRTP調用以上機器。

 class Example 
     { 
     public: 
      virtual std::shared_ptr<Example> clone() const = 0; 
      virtual void explain() const = 0; 
     }; 

     class Ex1 : public clonable<Example, Ex1> 
     { 
     public: 
      Ex1(const char * text) : m_text(text) {} 
      void explain() const override { std::cout << m_text; } 
     private: 
      const char * m_text; 
     }; 

     class Ex2 : public clonable<Ex1, Ex2> 
     { 
     public: 
      Ex2(const char * text, const char * extra) : parent(text), m_extra(extra) {} 
      void explain() const override { parent::explain(); std::cout << " " << m_extra; } 
     private: 
      const char * m_extra; 
     }; 

然而,這留下的基類需要實現根虛擬clone()方法,它意味着無處不在的層次結構中的克隆政策必須說明一遍又一遍。這當然是詛咒良好做法/常識/效率/正確性默認/等

所以,我算了一下,怎麼樣我作出這樣的工作,在一起的兩個CRTP模板,一個提供基類初始虛擬clone()用正確的簽名,然後使用它的父類以確定要使用正確的克隆政策派生CRTP,這樣一個只需要指定策略一次,根類,所有的派生類將實行正確clone()覆蓋智能地自行確定基類正在使用哪種策略。

不過,我想不通,是如何將策略模板模板暴露於所推導出CRTP模板,使他們不必採取任何明確的政策參數 - 以滿足設計理念。

// base class CRTP 
    template <typename base, template <typename, typename> typename policy = clone_raw> 
    class clonable_base : base 
    { 
    public: 
     // define our derived's parent class 
     using parent = clonable_base<base, policy>; 

     // constructor forwarding (enable all constructors) 
     using base::base; 

     using clone_policy = policy<base, base>; 
     using clone_type = typename clone_policy::return_type; 

     // clone using policy 
     virtual clone_type clone() const = 0 
     { 
      return clone_policy::clone(this); 
     } 
    }; 

所以這裏的百萬美元的問題是:我怎麼暴露policy模板模板下面衍生CRTP

// derived class CRTP with base CRTP helper 
    template <typename base, typename derived> 
    class clonable_derived : public base 
    { 
    public: 
     // define our derived's parent class 
     using parent = clonable_derived<base, derived>; 

     // constructor forwarding (enable all constructors) 
     using base::base; 

     using policy = base::policy; // ??? 

     using clone_policy = policy<base,derived>; 
     using clone_type = typename clone_policy::return_type; 

     // clone using policy 
     clone_type clone() const override 
     { 
      return clone_policy::clone(this); 
     } 
    }; 

一切都似乎是在地方,但我難倒如何暴露策略模板模板使派生可克隆類型可以訪問實例化一個合適的政策,他們的基地/導出對?!

回答

5

使用別名模板:

template <class A, class B> 
using policy = typename base::template policy<A,B>; 
+0

這看起來有希望的。我的頭目前正在爆炸「重寫虛函數返回類型不同並且不是協變」 - 如果我使用上面寫的代碼。缺少一些基本的東西。但是,謝謝:D – Mordachai

1

好 - 巴里的答案是正確的 - 模板的別名絕對是正確的工具。

我在我的原代碼的各種問題,包括周圍的事實,問題是一個不能更改虛擬的返回類型,除非它是協變,並unique_ptrshared_ptr似乎不支持協方差(至少在VC++ 2015更新3)。

所以,這裏的工作代碼 - 我肯定是要改進建議開啓!

// cloning policies: 
    // clone_raw  = new 
    // clone_shared = std::shared<> 
    // clone_unique = std::unique<> 

    template <class base, class derived> 
    struct clone_raw 
    { 
     using return_type = base *; 
     static return_type clone(const base * original) { return new derived(static_cast<const derived &>(*original)); } 
    }; 

    template <class base, class derived> 
    struct clone_shared 
    { 
     typedef std::shared_ptr<base> return_type; 
     static return_type clone(const base * original) { return std::make_shared<derived>(static_cast<const derived &>(*original)); } 
    }; 

    template <class base, class derived> 
    struct clone_unique 
    { 
     typedef std::unique_ptr<base> return_type; 
     static return_type clone(const base * original) { return std::make_unique<derived>(static_cast<const derived &>(*original)); } 
    }; 

    // base class CRTP 
    template <class base, template <class, class> typename policy = clone_raw> 
    class clonable_base 
    { 
    public: 

     // define our derived's parent class 
     using parent = clonable_base<base, policy>; 

     template <class derived> 
     using policy_alias = policy<base, derived>; 

     using clone_policy = policy_alias<base>; 
     using clone_type = typename clone_policy::return_type; 

     // clone using policy 
     virtual clone_type clone() const = 0; 
    }; 

    // derived class CRTP with base CRTP helper 
    template <typename base, typename derived> 
    class clonable_derived : public base 
    { 
    public: 
     // define our derived's parent class 
     using parent = clonable_derived<base, derived>; 

     // constructor forwarding (enable all constructors) 
     using base::base; 

     template <class derived> 
     using policy_alias = typename base::template policy_alias<derived>; 

     using clone_policy = typename policy_alias<derived>; 
     using clone_type = typename clone_policy::return_type; 

     // clone using policy 
     clone_type clone() const override 
     { 
      return clone_policy::clone(this); 
     } 
    }; 

下面是一個簡單的測試:

 class Example : public clonable_base<Example, clone_shared> 
     { 
     public: 
      virtual void explain() const = 0; 
     }; 

     class Ex1 : public clonable_derived<Example, Ex1> 
     { 
     public: 
      Ex1(const char * text) : m_text(text) {} 
      void explain() const override { std::cout << m_text; } 
     private: 
      const char * m_text; 
     }; 

     class Ex2 : public clonable_derived<Ex1, Ex2> 
     { 
     public: 
      Ex2(const char * text, const char * extra) : parent(text), m_extra(extra) {} 
      void explain() const override { parent::explain(); std::cout << " " << m_extra; } 
     private: 
      const char * m_extra; 
     }; 

int main() 
{ 
    Ex1 ex1("example 1"); 
    Ex2 ex2("example 2", "this is derived derived example"); 
    auto clone = ex2.clone(); 

    ex1.explain(); 
    std::cout << std::endl; 
    ex2.explain(); 
    std::cout << std::endl; 
    clone->explain(); 
    std::cout << std::endl; 

    return 0; 
} 
相關問題