5

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.

druckermanly
  • 2,694
  • 15
  • 27
  • 1
    **-1** the presented code is syntactically invalid (e.g. `Class`) – Cheers and hth. - Alf Jul 17 '14 at 04:42
  • 1
    and `Derived` is not a type in your pseudo code – quantdev Jul 17 '14 at 04:43
  • Sorry, fixed the litany of typos in my example. The current (updated) post is properly formatted. There's no template metaprogramming I can do? – druckermanly Jul 17 '14 at 04:45
  • 2
    @quantdev I think he means `Base`. user: Why can't you ditch CRTP and use `virtual` functions? And please don't reply *purrformance*. – Praetorian Jul 17 '14 at 04:45
  • Not "performance", it's the fact that I would need a templated pure virtual function. – druckermanly Jul 17 '14 at 04:47
  • Then how about describing the *problem* you're trying to solve, instead of [describing your attempted *solution*](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)? – Praetorian Jul 17 '14 at 04:48
  • "I've got a class Base". You've not. Base is not a class, it's a class template. "the function implementation is templated in my actual program". What's wrong with showing it in the vode snippet? – n. m. could be an AI Jul 17 '14 at 04:53
  • Why are you casting to the Derived type? Isn't that what virtual functions do for you automatically? I may be missing something but how is this different from the average, non-templated, bog-standard, run-of-the-mill polymorphic relationship between Base class and Derived class using virtual functions? – Galik Jul 17 '14 at 07:32
  • 1
    @Galik You can't have a templated (pure) virtual function. – druckermanly Jul 17 '14 at 08:27

4 Answers4

13

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; }
};
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
9

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>>
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Wow, despite my terrible explanation of my problem (I was in the midst of rewriting my question so that it was, at least, understandable...) you gave the exact answer I was looking for. Thank you so much! – druckermanly Jul 17 '14 at 05:20
  • You are very welcome. Take Alf's advice to heart: Use polymorphic types when it is appropriate. – jxh Jul 17 '14 at 05:22
  • What exactly `Base` is doing? Why `Derived` cannot inherit directly from `BaseInterface`? – n. m. could be an AI Jul 17 '14 at 05:42
  • @n.m.: Typically, the base class does not know what type was derived from. CRTP allows the base class to have that knowledge. `Base` is using CRTP while also inheriting from `BaseInterface`. If `Derived` inherits directly from `BaseInterface`, there is no way to call `implementation()`. – jxh Jul 17 '14 at 05:46
  • "there is no way to call implementation()". Of course there is a way, from Derived::interface. – n. m. could be an AI Jul 17 '14 at 05:52
  • @n.m.: Only because you are requiring each `Derived` to implement `interface()` separately to call `implementation()`. `Base` does that automatically. – jxh Jul 17 '14 at 05:54
  • "automatically". Not entirely. Derived must remember to pass itself and not some other class as the template parameter. This sounds silly, but people copy the `class Derived: public Base`, change *one* Derived to Derived1, compile cleanly and get mysterious crashes. – n. m. could be an AI Jul 17 '14 at 06:04
  • @n.m.: That is the essence of CRTP. You are arguing against using a well-known idiom/pattern. – jxh Jul 17 '14 at 06:05
  • CRTP is a kludge to work around a language deficiency, not a design pattern. Yes I pretty much argue against it. – n. m. could be an AI Jul 17 '14 at 06:09
  • 1
    @n.m.: Please take it up with Jim Coplien (and to be clear, I am not him). – jxh Jul 17 '14 at 06:12
  • Well sometimes it's reasonable (when you inject lots of stuff) but a single function isn't worth the risk IMHO. – n. m. could be an AI Jul 17 '14 at 06:50
2

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.

Mark Garcia
  • 17,424
  • 4
  • 58
  • 94
0

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.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243