A polymorphic collection of Curiously Recurring Template Pattern (CRTP) in C++?

╄→尐↘猪︶ㄣ 提交于 2020-01-01 00:52:07

问题


I've got a class Base from which I have two classes, DerivedA and DerivedB as defined below.

template <typename Derived>
class Base{
public:
    double interface(){
        static_cast<Derived*>(this)->implementation();
    }
};

class DerivedA : public Base<DerivedA>{
public:
    double implementation(){ return 2.0;}
};

class DerivedB : public Base<DerivedB>{
public:
    double implementation(){ return 1.0;}
};

In short, I'm trying to do the following to maintain a collection of objects, some of which are DerivedA and some of which are DerivedB:

std::vector<std::shared_ptr<Derived>>

Which is obviously impossible beacuse I've now made the class Derived a templated class.

Is there any way I can create / maintain a polymorphic collection of objects?

EDIT: Unfortunately, a simple templated structure does not work as the function implementation is templated in my actual program -- so then implementation would have to be a templated pure virtual function, which cannot be. Pardon my lack of explanation.


回答1:


Alf's suggestion is on target. It is easy to adapt it to your additional requirement. Define an interface with a pure virtual method:

struct BaseInterface {
    virtual ~BaseInterface() {}
    virtual double interface() = 0;
};

Now, your template base class can derive from the interface:

template <typename Derived>
class Base : BaseInterface {
public:
    double interface(){
        static_cast<Derived*>(this)->implementation();
    }
};

Now, you can create a vector of pointers to the interface:

std::vector<std::shared_ptr<BaseInterface>>



回答2:


This answer pertains to the question as it was at the time of this answer.


Don't use CRTP, which is not dynamic polymorphism, to create dynamic polymorphism.

Use a virtual function.

That's what they're for.

class Base
{
private:
    virtual
    auto implementation() -> double = 0;

public:
    auto interface() -> double { return implementation(); }
};

class DerivedA
    : public Base
{
private:
    auto implementation() -> double override { return 2.0; }
};


class DerivedB
    : public Base
{
private:
    auto implementation() -> double override { return 1.0; }
};



回答3:


Because Base<DerivedA> is a completely different type compared to Base<DerivedB>, you are right that you can't just do something like std::vector<std::shared_ptr<Base>>, as it would be syntactically invalid and has no meaningful semantics with regards to C++.

One way to achieve what you want and preserve your current CRTP hierarchy is to create a type erasing interface (or is it what it should be called? I'm not sure...). It is basically a wrapper that defines a certain interface in which you could wrap objects that obey that interface.

#include <vector>
#include <memory>
#include <iostream>


class VirtualBase {  // Really am not sure what it should be called, sorry
    class Interface {
    public:
        virtual ~Interface() = default;
        virtual double get() = 0;
    };

    template<typename T>
    class Impl : public Interface {
        T m_impl_obj;

    public:
        Impl(T impl_obj) : m_impl_obj(std::move(impl_obj)) {}

        double get() override {
            return m_impl_obj.get();
        }
    };

    std::shared_ptr<Interface> m_obj;

public:
    template<typename T>
    VirtualBase(T obj) : m_obj(new Impl<T>(std::move(obj))) {}

    double get() {
        return m_obj->get();
    }
};


template <typename Derived>
class Base{
public:
    double get(){
        return static_cast<Derived*>(this)->implementation();
    }
};

class DerivedA : public Base<DerivedA>{
public:
    double get(){ return 2.0;}
};

class DerivedB : public Base<DerivedB>{
public:
    double get(){ return 1.0;}
};


int main() {
    std::vector<VirtualBase> v;
    v.emplace_back(DerivedA{});
    v.emplace_back(DerivedB{});

    for(auto b : v) {
        std::cout << b.get() << std::endl;
    }
}

Live example

This is quite incomplete, but it should work, at least in my case if I need such a design. An excellent introduction, with explanations and rationales of how and why, is Sean Parent's talk at GoingNative 2013: http://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil . Really, you should see it, including all the other great presentations in GoingNative.




回答4:


Edit: this is based on an assumption that OP actually needs virtual function templates. This is apparently not the case.


It is not possible to have virtual function templates.

One way to partially simulate them is the Visitor pattern. It is often used to add dynamically dispatched functionality to existing classes. The fact that this added functionality can be templatized is overlooked all too often.

A pseudocode example

class Base
{
    virtual void accept (Visitor*) = 0;
}

class Derived1 : public Base
{
    void accept(Visitor* v) { v->visit(this); }
}

class Derived2 : public Base { void accept(Visitor* v) { v->visit(this); } }

class Visitor
{
     virtual void visit (Derived1*) = 0;
     virtual void visit (Derived2*) = 0;
}

template<class X, class Y>
class FooVisitor
{
   X x; Y y;
   FooVisitor(X x, Y y): x(x), y(y) {}

     void visit (Derived1*) { x.func(y); }
     void visit (Derived2*) { y.otherfunc(x); }
  }

Of course all the downsides of Visitor are here as well.



来源:https://stackoverflow.com/questions/24795012/a-polymorphic-collection-of-curiously-recurring-template-pattern-crtp-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!