Thread: virtual functions and templates

  1. #1
    Just because ygfperson's Avatar
    Join Date
    Jan 2002
    Posts
    2,490

    virtual functions and templates

    Code:
    template<typename T>
    T* clone(const T& t) {
      return new T(t);
    }
    
    //...
    
    class Base {  //this is an abstract base class
      virtual Base* new_object() const =0;
    }
    class Derived : public Base {
      virtual Base* new_object() const;
    }
    class Derived2 : public Derived {
      virtual Base* new_object() const;
    }
    Base* Derived::new_object() const {
      return clone(*this);
    }
    Both Derived class new_object functions call clone. (They look exactly the same.)
    I've got 3 questions
    1) If I did not repeat the definition or the declaration for Derived2, it would call Derived's function. *this would be a const Derived&, not a const Derived2& which I want. Is this correct?
    2) What if I just declared a virtual function in the header file for Derived2, but left the definition to Derived?
    3) What if I declared new_object in Base? (Of course, I would take away the '=0' part of it).

    Basically, I'm looking to see if there's a way to let the right typename be chosen so that clone() doesn't slice the object.

  2. #2
    Registered User
    Join Date
    May 2003
    Posts
    1,619

    Re: virtual functions and templates

    Hmm, it seems like you'd really rather do this; it makes more sense anyhow to have a covariant return:

    Code:
    class Base {  //this is an abstract base class
      virtual Base* new_object() const =0;
    }
    class Derived : public Base {
      virtual Derived* new_object() const;
    }
    class Derived2 : public Derived {
      virtual Derived2* new_object() const;
    }
    Derived* Derived::new_object() const {
      return clone(*this);
    }
    And you will have to explicity create each class's new_object(), because, as you said, Derived's is not adequate for Derived2's needs.

  3. #3
    Toaster Zach L.'s Avatar
    Join Date
    Aug 2001
    Posts
    2,686
    Its possible, but the mechanism to do so would be rather intricate, and hard to maintain. At least no way I can think of that wouldn't violate (read: completely run over) principles of OOD.
    The word rap as it applies to music is the result of a peculiar phonological rule which has stripped the word of its initial voiceless velar stop.

  4. #4
    Just because ygfperson's Avatar
    Join Date
    Jan 2002
    Posts
    2,490

    Re: Re: virtual functions and templates

    Originally posted by Cat
    Hmm, it seems like you'd really rather do this; it makes more sense anyhow to have a covariant return:

    Code:
    class Base {  //this is an abstract base class
      virtual Base* new_object() const =0;
    }
    class Derived : public Base {
      virtual Derived* new_object() const;
    }
    class Derived2 : public Derived {
      virtual Derived2* new_object() const;
    }
    Derived* Derived::new_object() const {
      return clone(*this);
    }
    And you will have to explicity create each class's new_object(), because, as you said, Derived's is not adequate for Derived2's needs.
    The problem with that way is that in non-overloaded classes, the return types must be the same.

    So, if a function is Derived's, and it is called from a Derived2 object, *this would be Derived&?

  5. #5
    Registered User
    Join Date
    May 2003
    Posts
    1,619

    Re: Re: Re: virtual functions and templates

    The problem with that way is that in non-overloaded classes, the return types must be the same.
    Not sure what you mean... this is legal C++ BTW:

    Code:
    class B{
    public:
      virtual B* F();
    };
    
    class D : public B{
    public:
      D* F();
    };
    And they're polymorphic just as you'd expect:

    B theObj = new D();
    theObj->F(); // calls D::F() not B::F()

    So, if a function is Derived's, and it is called from a Derived2 object, *this would be Derived&?
    Yes. That is why you need to implement this factory function in every class.

  6. #6
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Cat is totally corrent.....lookup "covariant return" - it's a relaxation on the return type as long as there's an inheritance between the returned types....Stroustrup discusses it breifly in The C++ Programming Language

    As you have probably realised, your problem is that if you forget to override the cloning function in a derived class, you will run into slicing problems as the dynamic type of the object will be incorrect.

    I dont know of a way to do this so that you can drop this concern, but I have seen a method that will alert you if a slicing situation does take place at runtime...

    Code:
    #include <exception>
    #include <typeinfo>
    #include <iostream>
    
    class MyException : public std::exception
    {
    public:
    	MyException(): szErr("Slicing Error"){}
    	virtual const char *what( ) const throw( ){return szErr;}
    private:
    	const char* szErr;
    };
    
    class base
    {
    public:
    	base* clone();
    	virtual void WhoAmI() = 0;
    private:
    	virtual base* vclone() = 0;
    
    };
    
    base* base::clone()
    {
    	base* b = vclone();
    	if(typeid(*b) != typeid(*this))
    		throw MyException();
    	return b;
    };
    
    class derived1 : public base
    {
    public:
    	void WhoAmI(){std::cout << "derived1" << std::endl;}
    private:
    	base* vclone(){return new derived1;}
    };
    
    class derived2 : public derived1
    {
    public:
    	void WhoAmI(){std::cout << "derived2" << std::endl;}
    private:
    	//no overridden virtual!
    };
    
    
    int main(void)
    {	
    	try
    	{
    		//excuse lack of resource management
    
    		base* b1 = new derived1;
    		b1->WhoAmI();
    		base* b1clone = b1->clone();		
    		b1clone->WhoAmI();
    		delete b1clone;
    		delete b1;
    
    		base* b2 = new derived2;
    		b2->WhoAmI();
    		base* b2clone = b2->clone();
    		b2clone->WhoAmI();
    		delete b2clone;
    		delete b2;
    
    	}
    	catch(std::exception& e)
    	{
    		std::cout << "Caught exception - " << e.what();
    	}
    	
    }
    In the above, clone is a public nonvirtual function that sits in base and calls a private virtual function (overridden in each derived class) to actually create the object. It then checks that the dynamic types are equal, if so it returns the pointer, if not (assming a derived class failed to overridde the vclone func and there's a case of slicing) it throws an exception.

    I dont know if there are many merits to this example, but I have seen it discussed a few times.

  7. #7
    Just because ygfperson's Avatar
    Join Date
    Jan 2002
    Posts
    2,490
    Cat is totally corrent.....lookup "covariant return" - it's a relaxation on the return type as long as there's an inheritance between the returned types....Stroustrup discusses it breifly in The C++ Programming Language
    Huh... didn't know that. Why was it implemented that way?

  8. #8
    Just because ygfperson's Avatar
    Join Date
    Jan 2002
    Posts
    2,490
    lol...
    Code:
    expression.hpp:440: sorry, not implemented: adjusting pointers for covariant returns
    Guess I need to update my compiler... (gcc 3.2, btw)

    //edit: the most recent compiler is the only one that can handle the "feature", and it has a bug
    Last edited by ygfperson; 07-22-2003 at 06:24 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Covariance with virtual functions and stl containers
    By cunnus88 in forum C++ Programming
    Replies: 4
    Last Post: 04-01-2009, 07:26 AM
  2. true virtual templates
    By Sebastiani in forum C++ Programming
    Replies: 11
    Last Post: 02-27-2006, 01:29 AM
  3. Class Templates and member functions
    By Mr_roboto in forum C++ Programming
    Replies: 7
    Last Post: 02-22-2006, 10:46 PM
  4. Questions about Templates
    By Shamino in forum C++ Programming
    Replies: 4
    Last Post: 12-18-2005, 12:22 AM