How to achieve a "virtual template function" in C ++

At first I read, and now I know that the function of a virtual template element (for now?) Is possible in C ++. A workaround would be to make the class a template, and then use the argument template also in the member function.

But in the context of OOP, I found that the example below would not be very “natural” if the class was actually a template. Note that the code does not actually work, but gcc-4.3.4 reports: error: templates may not be 'virtual'

 #include <iostream> #include <vector> class Animal { public: template< class AMOUNT > virtual void eat( AMOUNT amount ) const { std::cout << "I eat like a generic Animal." << std::endl; } virtual ~Animal() { } }; class Wolf : public Animal { public: template< class AMOUNT > void eat( AMOUNT amount) const { std::cout << "I eat like a wolf!" << std::endl; } virtual ~Wolf() { } }; class Fish : public Animal { public: template< class AMOUNT > void eat( AMOUNT amount) const { std::cout << "I eat like a fish!" << std::endl; } virtual ~Fish() { } }; class GoldFish : public Fish { public: template< class AMOUNT > void eat( AMOUNT amount) const { std::cout << "I eat like a goldfish!" << std::endl; } virtual ~GoldFish() { } }; class OtherAnimal : public Animal { virtual ~OtherAnimal() { } }; int main() { std::vector<Animal*> animals; animals.push_back(new Animal()); animals.push_back(new Wolf()); animals.push_back(new Fish()); animals.push_back(new GoldFish()); animals.push_back(new OtherAnimal()); for (std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) { (*it)->eat(); delete *it; } return 0; } 

So, making "Fish <Amount> foo" looks weird. However, it seems to me desirable to provide an arbitrary amount of food for each animal.

So I'm looking for a solution on how to achieve something like

 Fish bar; bar.eat( SomeAmount food ); 

This becomes especially useful when viewing a for loop. It would be possible to feed a certain amount (FoodAmount) to all different animals (via eat () and bind1st (), for example), it cannot be done so easily, although I am wounded I find it very intuitive (and therefore to some extent "Naturally "Although some may now object that this is due to a" homogeneous "-characteristic vector of the vector, I think / want it to be possible to achieve this, and I really would like to know how, since it has puzzled me for quite some time. ..

[EDIT]

To possibly clarify the motivation for my question, I want to program the Exporter class and give it different, more specialized classes. While the top-level Exporter class is usually for cosmetic / structural purposes only, the GraphExporter class is rendered, which should again serve as the base class for an even more specialized export. However, like the Animal example, I would like to be able to define GraphExporter * even on specialized / derived classes (for example, on SpecialGraphExplorer), but when calling "write (out_file)" it should call the corresponding member function for SpecialGraphExporter of GraphExporter :: write (out_file).

Perhaps this makes my situation and intentions clearer.

Best

Shadow

+29
c ++ virtual templates
May 03 '11 at 15:16
source share
9 answers

After some thought, I understood this as a classic multi-method requirement, that is, a method that dispatches more than one parameter based on the runtime type. Ordinary virtual functions are compared by single dispatch (and they are only dispatched by the type this ).

Refer to the following:

  • Andrei Alexandrescu wrote (seed bits for C ++?) About the implementation of multi-methods using generics in "Modern C ++ Design"
    • Chapter 11: "Multimethods" - he implements the basic multi-methods, making them logarithmic (using ordered lists), and then completely advances to multidimensional methods with constant time. Powerful enough material!
  • A code article that seems to have this implementation:
    • do not use drop types of any type (dynamic, static, reinterpret, const or C-style)
    • do not use RTTI;
    • lack of preprocessor;
    • strong type security;
    • separate compilation;
    • constant multimethod execution time;
    • no dynamic memory allocation (via new or malloc) during a call using several methods;
    • lack of use of non-standard libraries;
    • only standard C ++ functions are used.
  • C ++ Open Method Compiler , Peter Pirkelbauer, Yuri Solodky and Bjarn Straustrup
  • Loki library has MultipleDispatcher
  • Wikipedia has a pretty nice simple entry with examples in Multiple Dispatch in C ++.

Here is a simple approach from the wikipedia article for reference (a less simple approach scales to more derived types):

<sub>

 // Example using run time type comparison via dynamic_cast struct Thing { virtual void collideWith(Thing& other) = 0; } struct Asteroid : Thing { void collideWith(Thing& other) { // dynamic_cast to a pointer type returns NULL if the cast fails // (dynamic_cast to a reference type would throw an exception on failure) if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) { // handle Asteroid-Asteroid collision } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) { // handle Asteroid-Spaceship collision } else { // default collision handling here } } } struct Spaceship : Thing { void collideWith(Thing& other) { if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) { // handle Spaceship-Asteroid collision } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) { // handle Spaceship-Spaceship collision } else { // default collision handling here } } } 

sub>

+24
02 Oct '11 at 20:29
source share

Obviously, virtual member function templates are not allowed and cannot be implemented even theoretically. To create a virtual table of a base class, there must be a finite number of records of virtual function pointers. A function template allows an undefined number of "overloads" (i.e., instances).

Theoretically speaking, a language (e.g., C ++) could allow virtual member templates if it had some mechanism for specifying the actual (final) list of instances. C ++ has this mechanism (i.e., explicit instances of templates), so I think it could be done in the new C ++ standard (although I don’t know what problems may arise for compiler providers to implement this function ) But this is just a theoretical discussion, in practice, it is simply not allowed. The fact is that the number of virtual functions is limited (templates are not allowed).

Of course, this does not mean that the template cannot have virtual functions, nor does it mean that virtual functions cannot call function templates. Thus, there are many solutions in this vein (for example, a visitor template or other schemes).

One solution that seems to serve your purpose (although it’s hard to understand) elegantly looks like this (mostly a visitor’s template):

 #include <iostream> #include <vector> struct Eater { virtual void operator()(int amount) const = 0; virtual void operator()(double amount) const = 0; }; template <typename EaterType> struct Eater_impl : Eater { EaterType& data; Eater_impl(EaterType& aData) : data(aData) { }; virtual void operator()(int amount) const { data.eat_impl(amount); }; virtual void operator()(double amount) const { data.eat_impl(amount); }; }; class Animal { protected: Animal(Eater& aEat) : eat(aEat) { }; public: Eater& eat; virtual ~Animal() { delete &eat; }; }; class Wolf : public Animal { private: template< class AMOUNT > void eat_impl( AMOUNT amount) const { std::cout << "I eat like a wolf!" << std::endl; } public: friend struct Eater_impl<Wolf>; Wolf() : Animal(*(new Eater_impl<Wolf>(*this))) { }; virtual ~Wolf() { }; }; class Fish : public Animal { private: template< class AMOUNT > void eat_impl( AMOUNT amount) const { std::cout << "I eat like a fish!" << std::endl; } public: friend struct Eater_impl<Fish>; Fish() : Animal(*(new Eater_impl<Fish>(*this))) { }; virtual ~Fish() { }; }; int main() { std::vector<Animal*> animals; animals.push_back(new Wolf()); animals.push_back(new Fish()); for (std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) { (*it)->eat(int(0)); (*it)->eat(double(0.0)); delete *it; }; return 0; }; 

The above solution is neat because it allows you to determine the final number of overloads that you want in only one place (in the template of the Eater_impl class), and all you need in the derived class is a function template (and possibly an additional overload, for special cases). Of course, there is a bit of overhead, but I think that one could have fun in it to reduce overhead (additional storage of links and dynamic distribution of Eater_impl). I think that a curiously repeating pattern of a pattern could probably be used somehow for this purpose.

+10
May 03 '11 at 16:27
source share

I think a visitor pattern might be a solution.

UPDATE

I finished my example:

 #include <iostream> #include <vector> #include <boost/shared_ptr.hpp> class Animal; class Wolf; class Fish; class Visitor { public: virtual void visit(const Animal& p_animal) const = 0; virtual void visit(const Wolf& p_animal) const = 0; virtual void visit(const Fish& p_animal) const = 0; }; template<class AMOUNT> class AmountVisitor : public Visitor { public: AmountVisitor(AMOUNT p_amount) : m_amount(p_amount) {} virtual void visit(const Animal& p_animal) const { std::cout << "I eat like a generic Animal." << std::endl; } virtual void visit(const Wolf& p_animal) const { std::cout << "I eat like a wolf!" << std::endl; } virtual void visit(const Fish& p_animal) const { std::cout << "I eat like a fish!" << std::endl; } AMOUNT m_amount; }; class Animal { public: virtual void Accept(const Visitor& p_visitor) const { p_visitor.visit(*this); } virtual ~Animal() { } }; class Wolf : public Animal { public: virtual void Accept(const Visitor& p_visitor) const { p_visitor.visit(*this); } }; class Fish : public Animal { public: virtual void Accept(const Visitor& p_visitor) const { p_visitor.visit(*this); } }; int main() { typedef boost::shared_ptr<Animal> TAnimal; std::vector<TAnimal> animals; animals.push_back(TAnimal(new Animal())); animals.push_back(TAnimal(new Wolf())); animals.push_back(TAnimal(new Fish())); AmountVisitor<int> amount(10); for (std::vector<TAnimal>::const_iterator it = animals.begin(); it != animals.end(); ++it) { (*it)->Accept(amount); } return 0; } 

this prints:

 I eat like a generic Animal. I eat like a wolf! I eat like a fish! 
+7
May 03 '11 at 15:55
source share

Disabled virtual template function. However, you can use one or the other here.

You can create an interface using virtual methods and implement your various animals in terms of having an interface for food. (i.e. PIMPL)

Less human intuition will have a member function that is different from a member that is not a friend, as a free function that could take a template link to any animal and make them eat right.

You don't need templates here for the record. A pure virtual abstract method on the base class is enough to force and interact where all animals should eat, and determine how they do it with redefinition, provided that a regular virtual one is enough to say that all animals can eat, but if they don’t have then they can use this method by default.

+2
May 3, '11 at 15:32
source share

You can create a template class with a virtual function and implement the function in a derived class without using a template as follows:

 ah: template <class T> class A { public: A() { qDebug() << "a"; } virtual A* Func(T _template) { return new A;} }; bh: class B : public A<int> { public: B(); virtual A* Func(int _template) { return new B;} }; and the function CTOR and call A<int>* a1=new B; int x=1; a1->Func(x); 

Unfortunately, I did not find a way to create a virtual function with template parameters without declaring the class as a template and its template type in the dervied class

+2
Jul 15 '15 at 10:56
source share

I copied your code and changed it, so now it should work exactly the way you want:

  #include <iostream> #include <vector> //defined new enum type enum AnimalEnum { animal, wolf, fish, goldfish, other }; //forward declarations class Wolf; class Fish; class GoldFish; class OtherAnimal; class Animal { private: AnimalEnum who_really_am_I; void* animal_ptr; public: //declared new constructors overloads for each type of animal Animal(const Animal&); Animal(const Wolf&); Animal(const Fish&); Animal(const GoldFish&); Animal(const OtherAnimal&); template< class AMOUNT > /*removed the virtual keyword*/ void eat( AMOUNT amount ) const { switch (this->who_really_am_I) { case AnimalEnum::other: //You defined OtherAnimal so that it doesn't override the eat action, so it will uses it Animal eat case AnimalEnum::animal: std::cout << "I eat like a generic Animal." << std::endl; break; case AnimalEnum::wolf: ((Wolf*)this->animal_ptr)->eat(amount); break; case AnimalEnum::fish: ((Fish*)this->animal_ptr)->eat(amount); break; case AnimalEnum::goldfish: ((GoldFish*)this->animal_ptr)->eat(amount) break; } } void DeleteMemory() { delete this->animal_ptr; } virtual ~Animal() { //there you can choose if whether or not to delete "animal_ptr" here if you want or not } }; class Wolf : public Animal { public: template< class AMOUNT > void eat( AMOUNT amount) const { std::cout << "I eat like a wolf!" << std::endl; } virtual ~Wolf() { } }; class Fish : public Animal { public: template< class AMOUNT > void eat( AMOUNT amount) const { std::cout << "I eat like a fish!" << std::endl; } virtual ~Fish() { } }; class GoldFish : public Fish { public: template< class AMOUNT > void eat( AMOUNT amount) const { std::cout << "I eat like a goldfish!" << std::endl; } virtual ~GoldFish() { } }; class OtherAnimal : public Animal { //OtherAnimal constructors must be defined here as Animal constructors OtherAnimal(const Animal& a) : Animal(a) {} OtherAnimal(const Wolf& w) : Animal(w) {} OtherAnimal(const Fish& f) : Animal(f) {} OtherAnimal(const GoldFish& g) : Animal(g) {} OtherAnimal(const OtherAnimal& o) : Animal(o) {} virtual ~OtherAnimal() { } }; //OtherAnimal will be useful only if it has it own actions and members, because if not, typedef Animal OtherAnimal or using OtherAnimal = Animal can be used, and it can be removed from above declarations and below definitions //Here are the definitions of Animal constructors that were declared above/before: Animal::Animal(const Animal& a) : who_really_am_I(AnimalEnum::animal), animal_ptr(nullptr) {} Animal::Animal(const Wolf& w) : who_really_am_I(AnimalEnum::wolf), animal_ptr(new Wolf(w)) {} Animal::Animal(const Fish& f) : who_really_am_I(AnimalEnum::fish), animal_ptr(new Fish(f)) {} Animal::Animal(const GoldFish& g) : who_really_am_I(AnimalEnum::goldfish), animal_ptr(new GoldFish(g)) {} Animal::Animal(const OtherAnimal& o) : who_really_am_I(AnimalEnum::other), animal_ptr(new OtherAnimal(o)) {} int main() { std::vector<Animal> animals; animals.push_back(Animal()); animals.push_back(Wolf()); //Wolf is converted to Animal via constructor animals.push_back(Fish()); //Fish is converted to Animal via constructor animals.push_back(GoldFish()); //GoldFish is converted to Animal via constructor animals.push_back(OtherAnimal()); //OtherAnimal is converted to Animal via constructor for (std::vector<Animal>::const_iterator it = animals.begin(); it != animals.end(); ++it) { it->eat(); //this is Animal eat that invokes other animals eat //delete *it; Now it should be: it->DeleteMemory(); } animals.clear(); //All animals have been killed, and we don't want full vector of dead animals. return 0; } 
+1
Aug 04 '16 at
source share

In your scenario, you are trying to mix compile-time polymorphism with run-time polymorphism, but this cannot be done in this “direction”.

Essentially, your AMOUNT template argument is the expected interface for a type that is implemented based on the union of all the operations that each of them uses to use. If you want to create an abstract type that declares each of these operations, making them virtual when necessary, you could call it “eat” with different types (derived from your AMOUNT interface). And he will behave as expected.

0
Aug 04 '16 at 22:30
source share

In Mikael's post, I made another answer using CRTP and following the Eigen-style of using derived() for an explicit subclass:

 // Adaptation of Visitor Pattern / CRTP from: // http://stackoverflow.com/a/5872633/170413 #include <iostream> using std::cout; using std::endl; class Base { public: virtual void tpl(int x) = 0; virtual void tpl(double x) = 0; }; // Generics for display template<typename T> struct trait { static inline const char* name() { return "T"; } }; template<> struct trait<int> { static inline const char* name() { return "int"; } }; template<> struct trait<double> { static inline const char* name() { return "double"; } }; // Use CRTP for dispatch // Also specify base type to allow for multiple generations template<typename BaseType, typename DerivedType> class BaseImpl : public BaseType { public: void tpl(int x) override { derived()->tpl_impl(x); } void tpl(double x) override { derived()->tpl_impl(x); } private: // Eigen-style inline DerivedType* derived() { return static_cast<DerivedType*>(this); } inline const DerivedType* derived() const { return static_cast<const DerivedType*>(this); } }; // Have Child extend indirectly from Base class Child : public BaseImpl<Base, Child> { protected: friend class BaseImpl<Base, Child>; template<typename T> void tpl_impl(T x) { cout << "Child::tpl_impl<" << trait<T>::name() << ">(" << x << ")" << endl; } }; // Have SubChild extend indirectly from Child class SubChild : public BaseImpl<Child, SubChild> { protected: friend class BaseImpl<Child, SubChild>; template<typename T> void tpl_impl(T x) { cout << "SubChild::tpl_impl<" << trait<T>::name() << ">(" << x << ")" << endl; } }; template<typename BaseType> void example(BaseType *p) { p->tpl(2); p->tpl(3.0); } int main() { Child c; SubChild sc; // Polymorphism works for Base as base type example<Base>(&c); example<Base>(&sc); // Polymorphism works for Child as base type example<Child>(&sc); return 0; } 

Output:

 Child::tpl_impl<int>(2) Child::tpl_impl<double>(3) SubChild::tpl_impl<int>(2) SubChild::tpl_impl<double>(3) SubChild::tpl_impl<int>(2) SubChild::tpl_impl<double>(3) 

This snippet can be found here: repro: c808ef0: cpp_quick / virtual_template.cc

0
Apr 16 '17 at 17:56 on
source share

I do not work with templates, but I think:

(1) You cannot use templates inside a class, templates are more like global types or global variables.

(2) In OOP, the same problem that you present and that you are trying to solve with patterns can be solved with inheritance.

Classes work similarly to templates, you can expand them by adding new things or replace class classes with pointers, pointers to objects (AKA "links") and redefine virtual functions.

 #include <iostream> struct Animal { virtual void eat(int amount ) { std::cout << "I eat like a generic Animal." << std::endl; } virtual ~Animal() { } }; #if 0 // example 1 struct Wolf : Animal { virtual void eat(int amount) { std::cout << "I eat like a wolf!" << std::endl; } }; struct Fish : Animal { virtual void eat(int amount) { std::cout << "I eat like a fish!" << std::endl; } }; #else // example 2 struct AnimalFood { virtual int readAmount() { return 5; } virtual void showName() { std::cout << "I'm generic animal food" << std::endl; } }; struct PredatorFood : AnimalFood { virtual int readAmount() { return 500; } virtual void showName() { std::cout << "I'm food for a predator" << std::endl; } }; struct Fish : Animal { virtual void eat(AnimalFood* aFood) { if (aFood->readAmount() < 50) { std::cout << "OK food, vitamines: " << aFood->readAmount() << std::endl; } else { std::cout << "too much food, vitamines: " << aFood->readAmount() << std::endl; } } }; struct Shark : Fish { virtual void eat(AnimalFood* aFood) { if (aFood->readAmount() < 250) { std::cout << "too litle food for a shark, Im very hungry, vitamines: " << aFood->readAmount() << std::endl; } else { std::cout << "OK, vitamines: " << aFood->readAmount() << std::endl; } } }; struct Wolf : Fish { virtual void eat(AnimalFood* aFood) { if (aFood->readAmount() < 150) { std::cout << "too litle food for a wolf, Im very hungry, vitamines: " << aFood->readAmount() << std::endl; } else { std::cout << "OK, vitamines: " << aFood->readAmount() << std::endl; } } }; #endif int main() { // find animals Wolf* loneWolf = new Wolf(); Fish* goldenFish = new Fish(); Shark* sharky = new Shark(); // prepare food AnimalFood* genericFood = new AnimalFood(); PredatorFood* bigAnimalFood = new PredatorFood(); // give food to animals loneWolf->eat(genericFood); loneWolf->eat(bigAnimalFood); goldenFish->eat(genericFood); goldenFish->eat(bigAnimalFood); sharky->eat(genericFood); sharky->eat(bigAnimalFood); delete bigAnimalFood; delete genericFood; delete sharky; delete goldenFish; delete loneWolf; } 

Greetings.

-8
May 03 '11 at 15:39
source share



All Articles