现在,我知道内联没有保证,但是......
鉴于以下内容:
struct Base {
virtual int f() = 0;
};
struct Derived : public Base {
virtual int f() final override {
return 42;
}
};
extern Base* b;
我们有:
int main() {
return static_cast<Derived*>(b)->f();
}
编译为:
main:
movl $42, %eax
ret
...然而
int main() {
return (static_cast<Derived*>(b)->*(&Derived::f))();
}
编译为:
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl b, %eax
movl (%eax), %edx
movl %eax, (%esp)
call *(%edx)
leave
ret
真的很难过。
为什么PMF的调用没有内联? PMF是一个不变的表达式!
答案 0 :(得分:4)
这里的问题是,在第一种情况下,基于类型的devirtualization将间接调用转换为直接调用。
当您添加成员指针时,不能使用基于类型的devirtualization(因为它通过前端向下传递到有关所调用的类型和虚方法的优化信息,在本例中并非简单地知道)。 GCC
可以通过知道virutal table
是一个类并且知道只有在构造它之后才能调用它的成员,才能将实际访问权限永久地折叠到B
。目前它没有做这样的分析。
我建议填写GCC
bugzilla的增强请求。
答案 1 :(得分:1)
并不总是可以内联指向函数的指针(除非编译器能够弄清楚指针实际指向的是什么,这通常很困难,因此编译器可能会在您预期之前“放弃”)。
修改强>
扩展我的答案:所有编译器的首要任务是生成CORRECT代码(尽管有时这也不会发生!)。优化(例如内联函数)是编译器仅在“安全”时才会执行的操作。编译器可能不完全“理解”上面的表达式确实是一个常量表达式,因此可以回到“让我们做安全的事情”(通过虚函数表调用,而不是内联函数)。指向虚拟成员函数的指针在C ++中是一个非常棘手的主题。
答案 2 :(得分:0)
这确实有点烦人,但这并不奇怪。
大多数编译器转换通过模式匹配工作:识别模式并应用转换。那么这可能不会被优化的原因是什么?好吧,也许只是根据这种模式没有编写转换。
这里最具体的是,问题可能是&Derived::f
,gcc和clang都有一个特定的表示形式用于指向虚函数的指针:它们使用指向蹦床函数的指针来执行虚拟分辨率。因此,这个trampoline函数可能只是一个嵌套太多而无法让编译器查看。