我能否以某种方式公开模板模板参数?

时间:2017-02-13 19:47:55

标签: c++ templates clone crtp template-templates

我想为各种类层次结构提供适当的克隆机制。似乎是一个合理的想法,我拼凑了一个基本的解决方案,使用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;
        };

但是,这使得基类需要实现root虚拟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);
        }
    };

似乎所有内容都已到位,但我对如何公开策略模板模板感到困惑,以便派生的可克隆类型可以访问它以实例化其基本/派生对的相应策略?!!

2 个答案:

答案 0 :(得分:5)

使用别名模板:

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

答案 1 :(得分:1)

好的 - Barry的回答是正确的 - 模板别名绝对是正确的工具。

我的原始代码中存在各种问题,包括一个问题,即除非是协变的,否则无法更改虚拟的返回类型,unique_ptrshared_ptr似乎不支持协方差(至少不在VC ++ 2015 Update 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;
}