Search code examples
c++templatesclonecrtptemplate-templates

Can I expose a template template parameter somehow?


I would like to provide proper cloning machinery for various class hierarchies. Seems like a reasonable thought, and I pieced together a basic solution using CRTP to implement the necessary clone() functions in derived classes.

I further templated it with a template template argument to allow for policies to control the storage / ownership of the 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);
        }
    };

This works reasonably well, with each derived class needing to use CRTP to invoke the above machinery.

        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;
        };

However, this leaves the base class needing to implement the root virtual clone() method and it means that everywhere in the hierarchy the clone policy has to be stated over and over. This is of course anathema to good practices / common sense / efficiency / correctness by default / etc.

So, I figured, how about I make two CRTP templates that work together, one to provide the base class with the initial virtual clone() with the correct signature, and then a derived CRTP that uses its parent class to determine the correct clone policy to use, so that one need only specify the policy once, in the root class, and all derived classes would implement the correct clone() override intelligently determining for themselves which policy was being used by the base class.

However, what I cannot figure out, is how to expose the policy template template to the derived CRTP templates so that they don't need to take any explicit policy parameters - to fulfill the design idea.

    // 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);
        }
    };

So the million dollar question here is: how do I expose policy template template to the following derived 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);
        }
    };

Everything seems to be in place, but I'm stumped on how to expose the policy template template so that derived clonable types can access that to instantiate a the appropriate policy for their base/derived pairs?!!


Solution

  • Use an alias template:

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