6

Templated virtual member functions are not supported in C++ but I have a scenario where it would be ideal. Im wondering if someone has ideas for ways to accomplish this.

#include <iostream>


class Foo {
public:
    virtual void bar(int ){}
    // make a clone of my existing data, but with a different policy
    virtual Foo* cloneforDB() = 0;
};


struct DiskStorage {
    static void store(int x) { std::cout << "DiskStorage:" << x << "\n"; }
};

struct DBStorage {
    static void store(int x) { std::cout << "DBStorage:" << x << "\n"; }
};

template<typename Storage>
class FooImpl : public Foo {
public:
    FooImpl():m_value(0) {}
    template<typename DiffStorage>
    FooImpl(const FooImpl<DiffStorage>& copyfrom) {
        m_value = copyfrom.m_value;
    }
    virtual void bar(int x) {
        Storage::store(m_value);
        std::cout << "FooImpl::bar new value:" << x << "\n";
        m_value = x;
    }
    virtual Foo* cloneforDB() {
        FooImpl<DBStorage> * newfoo = new FooImpl<DBStorage>(*this);
        return newfoo;
    }
    int m_value;
};

int main()
{
    Foo* foo1 = new FooImpl<DiskStorage>();
    foo1->bar(5);
    Foo* foo2 = foo1->cloneforDB();
    foo2->bar(21);
}

Now if I want to clone the Foo implmemetation, but with a different Storagepolicy, I have to explicitly spell out each such implementation:

cloneforDB()
cloneforDisk()

A template parameter would have simplified that. Can anyone think of a cleaner way to do this? Please focus on the idea and not the example, since its obviously a contrived example.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
excalibur
  • 924
  • 2
  • 11
  • 26
  • I'm actually a bit confused and can not make sense of what you want to achieve, e.g., you define the `Storage` template parameter but don't use it anywhere inside the implementation of `FooImpl`. Maybe you can include your real use-case so we can help you? – SkyWalker Dec 20 '12 at 16:29
  • @GiovanniAzua: It's used in `virtual void bar(int x)` – icabod Dec 20 '12 at 16:34
  • @excalibur: Maybe you could use some variation of CRTP (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)? – icabod Dec 20 '12 at 16:36
  • http://stackoverflow.com/questions/2354210/can-a-member-function-template-be-virtual – Qortex Dec 20 '12 at 16:54

3 Answers3

7

Usually if you want to use a virtual template method, it means that something is wrong in the design of your class hierarchy. The high level reason for that follows.

Template parameters must be known at compile-time, that's their semantics. They are used to guarantee soundness properties of your code.

Virtual functions are used for polymorphism, ie. dynamic dispatching at runtime.

So you cannot mix static properties with runtime dispatching, it does not make sense if you look at the big picture.

Here, the fact that you store something somewhere should not be part of the type of your method, since it's just a behavioral trait, it could change at runtime. So it's wrong to include that information in the type of the method.

That's why C++ does not allow that: you have to rely on polymorphism to achieve such a behavior.

One easy way to go would be to pass a pointer to a Storage object as an argument (a singleton if you just want one object for each class), and work with that pointer in the virtual function.

That way, your type signature does not depend on the specific behavior of your method. And you can change your storage (in this example) policy at runtime, which is really what you should ask for as a good practice.

Sometimes, behavior can be dictated by template parameters (Alexandrescu's policy template parameters for example), but it is at type-level, not method level.

Qortex
  • 7,087
  • 3
  • 42
  • 59
  • @Mic...as I said before, dont focus on the logic of the example. For example, I could clone an existing object with a different access policy for a different user. There's no design issue there. Infact its 'unnatural' to take the access policy and make it a dynamic property just for this case. – excalibur Dec 20 '12 at 17:28
  • I understand, that's why I didn't focus on your `Storage` example. The thing is: you can't ask for dynamic behavior in a static compile-time feature. If you want to see it in another way: template is "for all" in mathematics, while you want to actually "instantiate" (or evaluate in mathematics) at runtime. It can't fit together. Here, you want to have an object semantics with polymorphism on a classname: to do that use a singleton instead of the classname. – Qortex Dec 20 '12 at 22:06
2

Just use templates all the way:

class Foo {
public:
    virtual void bar(int ){}

    template <class TargetType>
    Foo* clonefor() const;
};

class FooImpl { ... };

template 
inline <class TargetType>
Foo* Foo::clonefor() const
{
    return new FooImpl<TargetType>(*this);
}

Now call it:

int main()
{
    Foo* foo1 = new FooImpl<DiskStorage>();
    foo1->bar(5);
    Foo* foo2 = foo1->clonefor<DBStorage>();
    foo2->bar(21);
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
2

A trick I have sometimes used to get around this issue is this:

template<typename T>
using retval = std::vector<T const*>;
struct Bob {};

// template type interface in Base:
struct Base {
  template<typename T>
  retval<T> DoStuff();

  virtual ~Base() {};

// Virtual dispatch so children can implement it:
protected:
  virtual retval<int> DoIntStuff() = 0;
  virtual retval<double> DoDoubleStuff() = 0;
  virtual retval<char> DoCharStuff() = 0;
  virtual retval<Bob> DoBobStuff() = 0;
};

// forward template interface through the virtual dispatch functions:
template<> retval<int> Base::DoStuff<int>() { return DoIntStuff(); }
template<> retval<double> Base::DoStuff<double>() { return DoDoubleStuff(); }
template<> retval<char> Base::DoStuff<char>() { return DoCharStuff(); }
template<> retval<Bob> Base::DoStuff<Bob>() { return DoBobStuff(); }

// CRTP helper so the virtual functions are implemented in a template:
template<typename Child>
struct BaseHelper: public Base {
private:
  // In a real project, ensuring that Child is a child type of Base should be done
  // at compile time:
  Child* self() { return static_cast<Child*>(this); }
  Child const* self() const { return static_cast<Child const*>(this); }
public:
  virtual retval<int> DoIntStuff() override final { self()->DoStuff<int>(); }
  virtual retval<double> DoDoubleStuff() override final { self()->DoStuff<double>(); }
  virtual retval<char> DoCharStuff() override final { self()->DoStuff<char>(); }
  virtual retval<Bob> DoBobStuff() override final { self()->DoStuff<Bob>(); }
};

// Warning: if the T in BaseHelper<T> doesn't have a DoStuff, infinite
// recursion results.  Code and be written to catch this at compile time,
// and I would if this where a real project.
struct FinalBase: BaseHelper<FinalBase> {
  template<typename T>
  retval<T> DoStuff() {
    retval<T> ret;
    return ret;
  }
};

where I go from template-based dispatch, to virtual function dispatch, back to template based dispatch.

The interface is templated on the type I want to dispatch on. A finite set of such types are forwarded through a virtual dispatch system, then redispatched at compile time to a single method in the implementation.

I will admit this is annoying, and being able to say "I want this template to be virtual, but only with the following types" would be nice.

The reason why this is useful is that it lets you write type-agnostic template glue code that operates on these methods uniformly without having to do stuff like pass through pointers to methods or the like, or write up type-trait bundles that extract which method to call.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    Another method would be to avoid the n different virtual methods and just use one method with an index into a type list. The code to turn the type into an index and back again can be automatically generated from the type list. That would be slightly less efficient, yet cleaner. – Yakk - Adam Nevraumont Dec 21 '12 at 03:33
  • @exalibur Well, here is a basic run-time switch on a compile-time type list: http://ideone.com/IDsiLC -- using this technique, you can have a template method in the interface that extracts the index of the type, passes it to a virtual method, which using CRTP then does a run-time switch to re-extract the type and call a template method in the implementation class. I'm guessing `boost::mpl` has a more industrial grade version of this somewhere. – Yakk - Adam Nevraumont Dec 24 '12 at 17:36
  • @excalibur and here a second version where you can see me using CRTP to ferry a type from an interface to an impl class: http://ideone.com/QDuXKl -- the template base interface calls a virtual method, implemented in the CRTP helper, which then calls the template implementation method. – Yakk - Adam Nevraumont Dec 24 '12 at 17:49