调用未内联的常量指针到成员函数
Call to constant pointer-to-member function not being inlined
现在,我知道不能保证内联,但是...
鉴于以下情况:
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 是一个恒定的表达式!
这里的问题是,在第一种情况下,基于类型的去虚拟化将间接调用转换为直接调用。添加成员指针时,不能使用基于类型的去虚拟化(因为它通过前端传递有关所调用的类型和虚拟方法的优化信息来工作,而在这种情况下,这些信息并不容易知道)。 GCC
可以通过知道B
是一个类并且知道只有在构造它之后才能调用其成员,从而将实际访问不断折叠到virutal table
中。 目前它不做这样的分析。
我建议填写增强请求以GCC
bugzilla。
并不总是能够内联指向函数的指针(除非编译器能够弄清楚指针实际指向什么,这通常很困难,因此编译器可能会在您期望之前"放弃")。
编辑
扩展我的答案:所有编译器中的首要任务是生成正确的代码(尽管有时也不会发生这种情况!优化,例如内联函数,是编译器只有在"安全"时才会做的事情。编译器可能不太"理解"上面的表达式确实是一个常量表达式,因此回退到"让我们做安全的事情"(即通过虚函数表调用,而不是内联函数)。指向虚拟成员函数的指针在C++中是一个相当棘手的主题。
这确实有点烦人,但并不奇怪。
大多数编译器转换通过模式匹配工作:识别模式并应用转换。那么这可能不会优化的原因是什么?好吧,也许只是没有基于这种模式编写的转换。
最具体地说,问题可能与&Derived::f
有关,gcc 和 clang 都使用特定的表示来表示指向虚函数的指针:它们使用指向执行虚拟分辨率的蹦床函数的指针。因此,可能只是因为这个蹦床函数嵌套太多,编译器无法看穿。
- 如果基类包含双指针成员,则派生类的构造函数
- C++正确的指针成员初始化
- 是否可以使用智能指针成员设置具有另一个结构的结构?
- 为什么 operator() 处的指针成员不起作用?
- 更改队列指针成员的值需要在 C++ 中出现奇怪的错误
- 如何从另一个嵌套类中调用某个封闭类的嵌套类的函数指针成员的值?
- 结构对象的指针成员在传递给函数时被修改
- 参数的混合值,当我调用指针成员函数时
- 如何正确使用结构的共享指针成员?
- C++:私有类指针成员返回未定义的值
- 在函数中传递带有指针成员的结构是浅拷贝或深拷贝在 C 中
- 包含指针成员的嵌套结构
- 在 c++ 中,为什么 -> 被称为二进制中缀指针成员访问运算符?
- C++ 类指针成员行为奇怪(错误)
- 常量结构的指针成员
- 在类C++指针成员中
- 指针成员未在复制构造函数中初始化
- C++移动拥有指针成员的构造函数
- C++ 包含唯一指针成员变量的类的赋值运算符
- 如何使用 QPoint 指针成员对类进行排队和取消排队