Will this syntax avoid virtual calls?

Started by
11 comments, last by dmatter 16 years, 1 month ago
Just a quick question. Will the following syntax avoid the virtual call?

class Base {
public:
   virtual void F() { // do something here }
};

class Derived : public Base {
public:
   virtual void F() { // do something here }

   void g() {
       // is compiler smart enough to understand that I don't need runtime polymorphism here?
       this->Derived::F();
   }
}

Thanks /EDIT: Sorry I posted in the wrong forum
Advertisement
When in doubt about code generation, ask your compiler. Most compilers come with an option to compile to assembly. For example, with MSVC you can use the /FAs switch and with gcc you can use the -S switch.
Yes, it will avoid the use of the vtable.

When you're unsure, another option (for testing only) is to intentionally corrupt the vptr. If the call still works, then you know it's not using the vtable.

Here's one article on the topic: Smashing C++ Vptrs

Basically, in MSVC, the Vptr will be at the beginning of the class. With GCC, it's at the end. So, if you know there's a Vptr, you can intentionally write over the vptr with say, NULL. Then make your call and see what happens.

Techniques like this, while they should never be used in production code, are kind of cool for learning more about some of the deep internals of C++.
With the Derived:: specifier, the call is statically bound (that is, the compiler must call the version of the function that is present in the Derived class) and as such it is neither useful nor possible to use a virtual table to resolve the call.

Without it, the call is dynamically bound in order to allow calling of a function defined in a class which inherits from Derived, and will use the compiler's implementation of dynamic binding (usually, virtual functions).
Here's the generated assembly:

; 261  : 		// is compiler smart enough to understand that I don't need runtime polymorphism here?; 262  : 		this->Derived::F();  00023	8b 4d f8	 mov	 ecx, DWORD PTR _this$[ebp]  00026	e8 00 00 00 00	 call	 ?F@Derived@@UAEXXZ	; Derived::F


As far as I can tell, the call has avoided vftable. BTW, what a kinky idea smashing vptr is!

Thanks everybody
Whether or not this is a sensible thing to do depends on what exactly a class inheriting from Derived and implementing a new version of F() should expect from a call to g(), should it expect the call to behave as before or should it expect the call to behave differently because of the new implementation of F()?
I mostly use this method for code reuse. For the sake of discussion, let's say F() is a function that computes the size of a container via some non-trivial computations. g() on the other hand is a function that requires the size of this container to iterate over it. Besides, we definitely don't need runtime polymorphism here since we exactly know what implementation of the interface we need: the one that we are currently implementing. Therefore the best choice in my opinion is to use F() AND avoid the costs of a virtual call at the same time, which this syntax does. A more verbose approach is to factor the common code in another function and use that in both F() and g().
Quote:Original post by Ashkan
I mostly use this method for code reuse. For the sake of discussion, let's say F() is a function that computes the size of a container via some non-trivial computations. g() on the other hand is a function that requires the size of this container to iterate over it. Besides, we definitely don't need runtime polymorphism here since we exactly know what implementation of the interface we need: the one that we are currently implementing. Therefore the best choice in my opinion is to use F() AND avoid the costs of a virtual call at the same time, which this syntax does. A more verbose approach is to factor the common code in another function and use that in both F() and g().


The only question with this is whether or not this is the appropriate behavior in all cases. Can someone inherit from Derived? If so, then it could be logically appropriate that the second Derived class (DerivedDerived?) would have the desired implementation of F.
Quote:Original post by Ashkan
Besides, we definitely don't need runtime polymorphism here since we exactly know what implementation of the interface we need: the one that we are currently implementing.


No, you don't. You know that the implementation you need inherits from the one you are currently implementing.

As it stands, someone could inherit from Derived and change the behavior of the F function. For instance, using a multimap-like container to implement a map-like container where binding a key to a new value hides the old binding of that key until the new binding is removed—the old bindings are still being stored, but they do not count against the container's size nor do they appear in iterations.

If your function g assumes that Derived::F computes the number of elements in the container, then this assumption will be wrong and you will get strange behavior as a result.
Quote:Original post by Ashkan
Besides, we definitely don't need runtime polymorphism here since we exactly know what implementation of the interface we need: the one that we are currently implementing. Therefore the best choice in my opinion is to use F() AND avoid the costs of a virtual call at the same time, which this syntax does.

By doing this you're implicitly making it dangerous to inherit from Derived.

I say implicitly because there is nothing outwardly obvious from the class' interface to indicate that inheritance is dangerous (like a non-virtual destructor for example); in-fact the interface, being mostly virtual, makes it both reasonable and indeed inviting to believe that inheriting from Derived is safe and expected.

Quote:A more verbose approach is to factor the common code in another function and use that in both F() and g().

This new function should probably be virtual for the same reason that F() is virtual now.

This topic is closed to new replies.

Advertisement