我已经读过 Are inline virtual functions really a non-sense? .但我仍然有一些疑问,在那里没有找到答案。
他们说如果情况没有歧义,编译器应该内联虚函数。
但是:
This can happen only when the compiler has an actual object rather than a pointer or reference to an object.
如果我有一个从 A
派生的 B
类(它包含一个 virtual void doSth()
函数)并且我使用 B*
指针,而不是 A*
:
B* b = new B;
b->doSth();
- 假设
B
没有任何子类。很明显(在编译时)应该调用什么函数。所以可以内联。是真的吗? - 假设
B
有一些子类,但这些类没有自己的doSth()
函数。所以编译器应该“知道”唯一要调用的函数是B::doSth()
。我猜它不是内联的?
最佳答案
是否B
并不重要有任何派生类。在那种情况下b
指向 B
对象,以便编译器可以内联调用。
当然,任何体面的现代编译器都可以并且会在您的情况下做到这一点。如果你不使用指针,它会变得容易得多。那不是真正的“优化”。仅查看 .
左侧的 AST 节点,您就可以忽略虚拟调用这一事实变得显而易见。 -运算符(operator)。但是如果你使用指针,你需要跟踪指针对象的动态类型。但是现代编译器能够做到这一点。
编辑:一些实验是有序的。
// 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
}
// ...
可以看出,clang 确实直接调用了 f
, 两者都是 a
指向 A
当它指向 B
时. GCC 也是这样做的。
关于c++ - 如果我在明确的情况下使用指针,编译器可以内联虚函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6836401/