Is it possible to embed virtual functions

If I define the class as follows:

class A{ public: A(){} virtual ~A(){} virtual void func(){} }; 

Does this mean that the virtual destructor and func are inline

+8
c ++ class inline virtual-destructor
source share
2 answers

Regardless of whether the compiler chooses the built-in function defined by the built-in, it depends entirely on the compiler. In general, virtual functions can only be inlined when the compiler can either prove that the static type matches the dynamic type, or when the compiler can safely determine the dynamic type. For example, when you use a value of type A , the compiler knows that the dynamic type cannot be different, and it can inline the function. When using a pointer or a reference, the compiler usually cannot prove that the static type is the same, and virtual functions should usually follow normal virtual dispatch. However, even when a pointer is used, the compiler may have enough information from the context to find out the exact dynamic type. For example, MatthieuM. gave the following example:

 A* a = new B; a->func(); 

In this case, the compiler can determine that A points to an object B and thus invokes the correct version of func() without dynamically sending it. Without the need for dynamic dispatch, func() can be embedded. Of course, regardless of whether the compilers perform the appropriate analysis, it depends on its respective implementation.

As hvd is correctly pointed out, virtual sending can be bypassed by calling a virtual function, fully qualified, for example a->A::func() , in which case the virtual function can also be built-in. The main reason virtual functions are usually not enabled is the need for virtual sending. However, with full qualification, the function to be called is known.

+10
source share

Yes, and in several ways. You can see some examples of devirtualization in this letter. I posted to the Clang mailing list about 2 years ago.

Like all optimizations, this expects the compiler to eliminate alternatives: if it can prove that the virtual call is always allowed in Derived::func , then it can call it directly.

There are various situations, we will start first with semantic evidence:

  • SomeDerived& d , where SomeDerived is final , allows you to devirtualize all method calls
  • SomeDerived& d , d.foo() , where foo is final , also allows the virtualization of this particular call

Then there are situations when you know the dynamic type of an object:

  • SomeDerived d; => dynamic type d required SomeDerived
  • SomeDerived d; Base& b; => dynamic type b required SomeDerived

These 4 devirtualization situations are usually resolved by the compiler because they require fundamental knowledge about the semantics of the language. I can confirm that all 4 are implemented in Clang, and I think they are also implemented in gcc.

However, there are many situations where this breaks down:

 struct Base { virtual void foo() = 0; }; struct Derived: Base { virtual void foo() { std::cout << "Hello, World!\n"; }; void opaque(Base& b); void print(Base& b) { b.foo(); } int main() { Derived d; opaque(d); print(d); } 

Although it is obvious here that the foo call is resolved before Derived::foo , Clang / LLVM will not optimize it. The problem is that:

  • Clang (front-end) does not perform inlining, so it cannot replace print(d) with d.foo() and devirtualize the call
  • LLVM (back-end) does not know the semantics of the language, so even after replacing print(d) with d.foo() it assumes that the virtual pointer d could be changed to opaque (the definition is opaque, as the name implies)

I watched the efforts on the Clang and LLVM mailing lists, as both sets of developers talked about losing information and how to get Clang to tell LLVM: "Everything is fine," but unfortunately the problem is not trivial and has not yet been resolved. .. thus, half-inspired devirtualization in the interface in order to try to get all the obvious cases, and some not so obvious (although, by agreement, the interface is not where you implement them).


For reference, the code for devirtualization in Clang can be found in CGExprCXX.cpp in a function called canDevirtualizeMemberFunctionCalls . It is only ~ 64 lines (right now) and carefully commented out.

+4
source share

All Articles