Karu
Karu

Reputation: 4627

Is there a way to not inherit "virtualness" of a function in a subclass?

Is it possible in C++ to have a class override a virtual function, but only have virtual dispatch when the function is called through the superclass (ie. not when it is called on something statically typed as the subclass)? I know this isn't what happens, but is there any way to achieve something close?

The reason for wanting this is that I have two classes which both expose a flush() function. The vast majority of the time in my program, I am calling flush() directly on a subclass object that I know the type of, so I don't need virtual dispatch. However I want to add a superclass into the mix so that very infrequently I can pass a reference to an instance of either one of the classes into a doSomethingThenFlush() function, which would call flush() virtually.

I know I could use templates instead of virtual functions, and I know I could have two different functions (eg. flushVirtual() which just called flushNonVirtual(), and call flushNonVirtual() everywhere I don't need virtual dispatch). But these both seem a bit like throwing code at a problem which is largely syntactical. Is there any more elegant way to achieve this?

Perhaps more importantly, does anyone know why virtualness is inherited in C++?

struct Base
{
  virtual ~Base(){}
  virtual void func();
};

struct Derived : public Base
{
  void func(){}
};

void callVirtually(Base &base)
{
  base.func();//this will use virtual dispatch
}

void callStatically(Derived &derived)
{
  derived.func();//I don't want/need this to use virtual dispatch
}

int main()
{
  Derived derived;
  callVirtually(derived);
  callStatically(derived);
}

Upvotes: 9

Views: 382

Answers (4)

Matthieu M.
Matthieu M.

Reputation: 299740

In C++03, no.

As others said, it is a compiler optimization (and a frequently used one) to de-virtualize he call whenever it can assess that the runtime type of the object.

However, in C++0x we get two new keywords: override and final and both can be applied to member functions (final can also be applied to a class).

  • override: specify that this function overrides a virtual function in a base class, useful to get warned when this is not the case
  • final: specify that this function (virtual) cannot be overriden in children classes.

Your class would thus become:

struct Derived : public Base
{
  void func() final {}
};

Note: using final does not mandate that the compiler devirtualize function calls (from the Standard point of view), but any compiler worth its salt should do so.

Upvotes: 5

Nemo
Nemo

Reputation: 71515

In your specific example, if callStatically gets inlined, the compiler will probably avoid the virtual function dispatch because it can see the object's actual type (because it is a local variable).

Probably your compiler can also avoid a virtual dispatch for cases like this:

class Foo {
public:
    callStatically() { d.func() }
private:
    Derived d;
};

The compiler is likely to perform this optimization whether or not callStatically is inlined, because it can see the actual type of the variable member.

But to my knowledge, there is no way to force the compiler to bypass the virtual call in general.

Upvotes: 4

iammilind
iammilind

Reputation: 69978

The answer lies in your question.

derived.func();  // no virtual dispatch

When you call a virtual function using object, there is no virtual dispatch. It calls the function using the static-type of the object.

virtual function comes into the picture only when you try to call a function using a pointer or a reference.

Edit: In your updated question, you are using Derived& to call func(). Calling by reference will make sure that virtual dispatch happens. So there is no language facility (like final in Java), which will stop virtual dispatch.

Upvotes: 3

Seth Carnegie
Seth Carnegie

Reputation: 75130

Virtualness is inherited because you don't know if someone is going to derive further from your Derived. Someone could just as well make a MoreDerived which could be passed to a function expecting a Derived&, and they'd be sad when they found that it was Derived's versions of all your virtual functions were bieng called instead of MoreDerived's.

If you mean that you won't ever inherit from Derived so you don't want to pay for a virtual function call, then you're out of luck, because C++ provides no way to promise that you won't ever inherit from a class, which would be necessary to do what you are wanting.

Upvotes: 8

Related Questions