问题
If I define a class like this:
class A{
public:
A(){}
virtual ~A(){}
virtual void func(){}
};
Does it mean that that the virtual destructor and func
are inlined
回答1:
Whether the compiler chooses to inline a function which is defined inline is entirely up to 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 generally cannot prove that the static type is the same and virtual
functions generally need to follow the usual virtual dispatch. However, even when a pointer is used, the compiler may have enough information from the context to know the exact dynamic type. For example, MatthieuM. gave the following exmaple:
A* a = new B;
a->func();
In this case the compiler can determine that a
points to a B
object and, thus, call the correct version of func()
without dynamic dispatch. Without the need for the dynamic dispatch, func()
could then be inlined. Of course, whether compilers do the corresponding analysis depends on its respective implementation.
As hvd correctly pointed out, the virtual dispatch can be circumvented by calling a virtual function will full qualification, e.g., a->A::func()
, in which case the virtual function can also be inlined. The main reason virtual functions are generally not inlined is the need to do a virtual dispatch. With the full qualification the function to be called is, however, known.
回答2:
Yes, and in multiple ways. You can see some examples of devirtualization in this email I sent to the Clang mailing list about 2 years ago.
Like all optimizations, this is pending the compiler abilities to eliminate alternatives: if it can prove that the virtual call is always resolved in Derived::func
then it can call it directly.
There are various situations, let us start first with the semantic evidences:
SomeDerived& d
whereSomeDerived
isfinal
allows to devirtualization of all method callsSomeDerived& d
,d.foo()
wherefoo
isfinal
also allows devirtualization of this particular call
Then, there are situations where you know the dynamic type of the object:
SomeDerived d;
=> the dynamic type ofd
is necessarilySomeDerived
SomeDerived d; Base& b;
=> the dynamic type ofb
is necessarilySomeDerived
Those 4 devirtualization situations are usually solved by the compiler front-end because they require fundamental knowledge about the language semantics. I can attest that all 4 are implemented in Clang, and I would think they are also implemented in gcc.
However, there are plenty of 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);
}
Even though here it is obvious that the call to foo
is resolved to Derived::foo
, Clang/LLVM will not optimize it. The issue is that:
- Clang (front-end) does not perform inlining, thus it cannot replace
print(d)
byd.foo()
and devirtualize the call - LLVM (back-end) does not know the semantics of the language, thus even after replacing
print(d)
byd.foo()
it assumes that the virtual pointer ofd
could have been changed byopaque
(whose definition is opaque, as the name implies)
I've followed efforts on the Clang and LLVM mailing list as both sets of developers reasoned about the loss of information and how to get Clang to tell LLVM: "it's okay" but unfortunately the issue is non-trivial and has not been solved yet... thus the half-assed devirtualization in the front-end to try and get all obvious cases, and some not so obvious ones (even though, by convention, the front-end is not where you implement them).
For reference, the code for the devirtualization in Clang can be found in CGExprCXX.cpp in a function called canDevirtualizeMemberFunctionCalls
. It's only ~64 lines long (right now) and thoroughly commented.
来源:https://stackoverflow.com/questions/18432040/can-virtual-functions-be-inlined