我可以以某种方式公开模板模板参数吗?

Can I expose a template template parameter somehow?

我想为各种 class 层次结构提供适当的克隆机制。似乎是一个合理的想法,我拼凑了一个使用 CRTP 的基本解决方案,以在派生的 classes 中实现必要的 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);
        }
    };

这工作得相当好,每个派生 class 需要使用 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;
        };

然而,这使得基础 class 需要实现根虚拟 clone() 方法,这意味着在层次结构中的任何地方都必须一遍又一遍地声明克隆策略。这当然是对良好实践/常识/效率/默认正确性/等的诅咒

所以,我想,我制作两个协同工作的 CRTP 模板如何,一个为基础 class 提供带有正确签名的初始虚拟 clone(),然后是派生的 CRTP它使用其父 class 来确定要使用的正确克隆策略,因此只需在根 class 中指定一次策略,所有派生的 classes 都会实施正确的clone() 覆盖智能地自行确定基地正在使用的策略 class。

但是,我想不通的是,如何将策略模板暴露给派生的 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);
        }
    };

一切似乎都已到位,但我对如何公开策略模板模板感到困惑,以便派生的可克隆类型可以访问它以实例化适当的策略他们 base/derived 对?!!

使用别名模板:

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

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

我的原始代码中存在各种问题,包括无法更改虚拟的 return 类型这一事实的问题,除非它是协变的,以及 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;
}