6

现在,我知道内联不能保证,但是......

鉴于以下情况:

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 是一个常数表达式!

4

3 回答 3

5

这里的问题是,在第一种情况下,基于类型的去虚拟化将间接调用变成了直接调用。当你添加一个成员指针时,不能使用基于类型的去虚拟化(因为它通过前端向下传递到关于被调用的类型和虚拟方法的优化信息来工作,在这种情况下,这些信息并不容易知道)。 通过知道这是一个类并且知道它的成员只有在它被构造后才能被调用,GCC可能能够不断地折叠实际的访问。目前它不做这样的分析。virutal tableB

我建议向GCCbugzilla 填写增强请求。

于 2013-09-29T19:19:39.663 回答
1

内联指向函数的指针并不总是可能的(除非编译器能够弄清楚指针实际指向的内容,这通常很困难,因此编译器可能会在您期望它之前“放弃”)。

编辑

扩展我的答案:所有编译器的首要任务是生成正确的代码(尽管有时这也不会发生!)。诸如内联函数之类的优化是编译器仅在“安全”时才会执行的操作。编译器可能不太“理解”上面的表达式确实是一个常量表达式,因此退回到“让我们做安全的事情”(即通过虚函数表调用,而不是内联函数)。指向虚成员函数的指针在 C++ 中是一个相当棘手的主题。

于 2013-09-29T15:33:48.087 回答
0

这确实有点烦人,但也不足为奇。

大多数编译器转换通过模式匹配工作:识别模式并应用转换。那么这可能没有优化的原因是什么?好吧,也许只是没有基于这种模式编写的转换。

最具体地说,问题可能是关于&Derived::f,gcc 和 clang 都使用特定的表示形式来表示指向虚函数的指针:它们使用指向执行虚解析的蹦床函数的指针。因此,可能只是这个蹦床函数嵌套太多,编译器无法看穿。

于 2013-09-29T17:18:51.810 回答