2017-02-13 215 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; 
} 
相关问题