som*_*ock 10 c++ compiler-construction virtual inline
我已经阅读过内联虚拟功能真的没有意义吗?.但我仍有一些疑虑,并没有找到答案.
他们说如果情况不明确,编译器应该内联虚函数.
然而:
只有当编译器具有实际对象而不是指针或对象的引用时,才会发生这种情况.
那么,如果我有一个B派生自一个类的类A(包含一个virtual void doSth()函数)并使用B*指针,而不是A*:
B* b = new B;
b->doSth();Run Code Online (Sandbox Code Playgroud)
B没有任何子类.在编译时应该调用什么函数是相当明显的.所以可以内联.实际上是吗?B有一些子类,但这些类没有自己的doSth()功能.所以编译器应该"知道"唯一要调用的函数B::doSth().我猜它虽然没有内联?Joh*_*itb 14
是否B有任何派生类并不重要.在那种情况下b指向一个B对象,因此编译器可以内联调用.
当然,任何体面的现代编译器都可以并且将会在您的情况下执行此操作.如果你不使用指针,那就变得容易多了.那不是真正的"优化".通过查看.-operator 左侧的AST节点,您可以省略虚拟调用的事实变得明显.但是如果使用指针,则需要跟踪指针的动态类型.但现代编译器能够做到这一点.
编辑:一些实验是有序的.
// main1.cpp
struct A {
virtual void f();
};
struct B : A {
virtual void f();
};
void g() {
A *a = new A;
a->f();
a = new B;
a->f();
}
// clang -O2 -S -emit-llvm -o - main1.cpp | c++filt
// ...
define void @g()() {
%1 = tail call noalias i8* @operator new(unsigned int)(i32 4)
%2 = bitcast i8* %1 to %struct.A*
%3 = bitcast i8* %1 to i32 (...)***
store i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*]* @vtable for A, i32 0, i32 2) to i32 (...)**), i32 (...)*** %3, align 4
tail call void @A::f()(%struct.A* %2)
%4 = tail call noalias i8* @operator new(unsigned int)(i32 4)
%5 = bitcast i8* %4 to i32 (...)***
store i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*]* @vtable for B, i32 0, i32 2) to i32 (...)**), i32 (...)*** %5, align 4
%tmp = bitcast i8* %4 to %struct.B*
tail call void @B::f()(%struct.B* %tmp)
ret void
}
// ...
Run Code Online (Sandbox Code Playgroud)
可以看出,f当a指向a A和指向a 时,clang会直接调用B.海湾合作委员会也这样做.