Virtual Function Template Virtual C ++

The virtual member template functions are not supported in C ++, but I have a scenario where this would be ideal. I am wondering if anyone has any ideas on how to do 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 Foo implmemetation, but with a different Storagepolicy, I must explicitly describe each such implementation:

 cloneforDB() cloneforDisk() 

The template parameter will simplify this. Can anyone think of a cleaner way to do this? Please focus on the idea, not the example, as its clearly a contrived example.

+4
source share
3 answers

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

Template parameters must be known at compile time what their semantics are. They are used to guarantee the sound quality of your code.

Virtual functions are used for polymorphism, i.e. dynamic dispatch at runtime.

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

Here, the fact that you are storing something somewhere somewhere should not be part of the type of your method, since it is just a behavioral attribute, it can change at runtime. Therefore, it is incorrect to include this information in the type of method.

This is why C ++ does not allow this: you must rely on polymorphism to achieve this behavior.

One simple way is to pass a pointer to the Storage object as an argument (singleton if you need only one object for each class), and work with this pointer in a virtual function.

Thus, your type signature does not depend on the specific behavior of your method. And you can change the retention policy (in this example) at runtime, which is really necessary for you to recommend as good practice.

Sometimes the behavior may be dictated by template parameters (for example, Alexandrescu template parameters), but it is at the level level, and not at the method level.

+9
source

Just use the templates:

 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); } 
+2
source

The trick I sometimes used to get around this problem 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 sending to virtual function sending, back to template-based sending.

The interface is templated by the type I want to send. A finite set of these types is sent through the virtual dispatch system, and then redistributed at compile time to one method in the implementation.

I admit that this is annoying and may say: "I want this template to be virtual, but only with the following types" would be nice.

The reason this is useful is because it allows you to write an agnostic-type glue type that works uniformly on these methods, without having to do things like passing pointers to methods or the like, or write down type pools types that retrieve this method to call.

+1
source

All Articles