Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Virtual Calls using address of pure virtual member. Is it legal?

I read sometime back (probably on c.l.c++.moderated) that virtual function calls can be templatized. I tried something on the following lines.

#include <iostream>

template<class T, class FUN> 
void callVirtual(T& t, FUN f){ 
   (*t.*f)(); 
} 


struct Base{ 
   virtual ~Base(){} 
   virtual void sayHi()=0; 
}; 


struct Derived : public Base{ 
   void sayHi(){ 
      std::cout << "Hi!" << std::endl; 
   } 
}; 


void Test(){ 
   Base* ptr = new Derived; 
   callVirtual(ptr,&Base::sayHi); 
} 

int main()
{
   Test();
   return 0;
}

Output:
Hi!

The templatized method though given the address of pure virtual base member method at compile time calls the correct method at runtime. Is it legal in standard C++ to take address of a pure virtual member?

Thanks in advance

EDIT-1: I removed the second part of the question 'how does it work?'. Looks like that is what is catching attention.

EDIT-2: I searched c.l.c++.moderated and came across this link (http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/5ddde8cf1ae59a0d). The consensus seems like since the standard does not restrict it, it is vaild.

EDIT-3: After reading the codeproject article (thanks to ovanes), i am thinking that the compilers do some magic. Since virtual functions are implemented via vtable (which is compiler specific), taking address of a virtual function always gives the offset in the vtable. Depending on the 'this' pointer used, the appropriate function (whose address is at the offset) is called. I am not sure how to vindicate this though as the standard does not say anything abt it!

like image 311
Abhay Avatar asked Jun 09 '09 06:06

Abhay


People also ask

Can you call a pure virtual function?

You may call a virtual function as long as it is not a pure virtual function, and as long as you know that what you are calling is the method from the class itself and not expecting any polymorphism. Calling a pure virtual function from a constructor is undefined behaviour even if it has an implementation.

What does pure virtual function call mean?

A pure virtual function is a function that must be overridden in a derived class and need not be defined. A virtual function is declared to be “pure” using the curious =0 syntax. For example: class Base {

What is difference between pure virtual and virtual?

A virtual function is a member function of base class which can be redefined by derived class. A pure virtual function is a member function of base class whose only declaration is provided in base class and should be defined in derived class otherwise derived class also becomes abstract.

Can we call virtual method from constructor?

You can call a virtual function in a constructor, but be careful. It may not do what you expect. In a constructor, the virtual call mechanism is disabled because overriding from derived classes hasn't yet happened. Objects are constructed from the base up, “base before derived”.


2 Answers

Sure it is. Your code is no different than merely calling the pure virtual method like this:

void Test()
{ 
   Base* ptr = new Derived; 
   ptr->sayHi();
   delete ptr;
} 

The only difference is that you have another mechanism for doing the call, and in this case through callVirtual().

like image 68
ralphtheninja Avatar answered Oct 28 '22 00:10

ralphtheninja


As Magnus Skog said, the template part isn't really relevant. What it boils down to is that:

(ptr->* &Base::sayHi)()

seems to work, but

ptr->Base::sayHi()

obviously doesn't because sayHi is pure virtual.

I haven't been able to find anything in the standard about what happens when you take the address of a virtual, or pure virtual, function though. I'm not sure it's legal. It works in GCC and MSVC though, and Comeau's online compiler doesn't complain either.

Edit

Even if it is valid, as your edits say, I'm still wondering what it means.

If we assume for simplicity that sayHi is non-pure (so a definition of Base::sayHi exists), then what happens if I take the address of it? Do I get the address of Base::sayHi, or the address of the function that the vtable points to (Derived::sayHi in this case)?

Apparently, compilers assume the latter, but why? Calling ptr->Base::sayHi() calls sayHi in the base class, but taking the address of Base::sayHi gives me the address of Derived::sayHi

It seems inconsistent to me. Is there some rationale behind this that I'm missing?

like image 38
jalf Avatar answered Oct 28 '22 00:10

jalf