How to achieve “virtual template function” in C++

后端 未结 9 1621
囚心锁ツ
囚心锁ツ 2020-11-28 03:20

first off: I have read and I know now that a virtual template member function is not (yet?) possible in C++. A workaround would be to make the class a template and then use

相关标签:
9条回答
  • 2020-11-28 04:13

    I think the visitor pattern can 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!
    
    0 讨论(0)
  • 2020-11-28 04:15

    I have copied your code and modified it, so now it should work exactly as 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's Animal's 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's 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's 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's 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;
            }
    
    0 讨论(0)
  • 2020-11-28 04:16

    I don't 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 O.O.P., the same problem you present, and that you are trying to solve by using templates, can be solved by using inheritance.

    Classes work similar to templates, you can extended by adding new things, or replace things of classes with pointers, pointers to objects (A.K.A. "references") and overriding 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;
    }
    

    Cheers.

    0 讨论(0)
提交回复
热议问题